﻿
var infodiv = 110;
var itemdraw;
(function ($) {
    $.tickp = function (target, options) {
        return new $.tickp.fn.init(target, options);
    };
    $.tickp.fn = {
        emas: {},
        psar: [],
        current: undefined,
        prev: undefined,
        loffset: 10,
        rmargin: 30,
        plotwidth: function () {
            var elmnt = document.getElementsByClassName("main-chart");
            var cWidth = elmnt[0].offsetWidth;
            if (cWidth >= 720)
                cWidth = 720;
            else
                cWidth = cWidth - 40;            
            return cWidth;
        },
        width: undefined,
        tmargin: 10,
        ttextrow: 10,
        topmargin: undefined,
        vspacing: 0, //khoang cach giua cac chart
        limargin: 10,
        liplotht: 48,  // height khung indicator
        loweriht: undefined,
        bmargin: 0,
        plotht: 144,  // heigh khung price chart
        height: undefined,
        interval: 0,
        mode: 1, // 0: navigation 1: pan and zoom 

        // supported indicators 
        supported: ['ema', 'sma', 'psar', 'bbands', 'rsi', 'stoch', 'macd',
                    'adx', 'uo', 'momentum', 'mfi', 'cmf', 'perR', 'obv',
                    'atr', 'cci'],
        copyrighttext: "",
        init: function (target, options) {
            this.target = $$(target);
            this.cs = $.tickp.csdark;
            this.width = this.loffset + this.rmargin + this.plotwidth();
            this.topmargin = this.tmargin + this.ttextrow,
            this.loweriht = this.vspacing + this.limargin + this.liplotht,
            this.height = this.topmargin + this.plotht + this.loweriht + this.bmargin;
            this.lines = [];
            this.rectangles = [];
            this.fibfans = [];
            this.eclipses = [];
            this.fibrecs = [];
            this.shapes = [];
            this.lastitem = [];
            this.infodiv = document.createElement('div')
            this.infodiv.id = 'info';
            this.infodiv.zIndex = "1000";
            this.target.appendChild(this.infodiv);
            window.$plot = this;
            return this;
        },

        drawshapes: function () {
            this.octx.clearRect(0, 0, this.width, this.height);
            this.octx.strokeStyle = plot.cs.stroke;
            var lines = this.lines;
            for (var i = 0; i < lines.length; i++) {
                this.octx.beginPath();
                this.octx.moveTo(lines[i][0][0], lines[i][0][1]);
                this.octx.lineTo(lines[i][1][0], lines[i][1][1]);
                this.octx.stroke();
            }
            var rectangles = this.rectangles;
            for (var i = 0; i < rectangles.length; i++) {
                this.octx.beginPath();
                this.octx.strokeRect(rectangles[i][0][0], rectangles[i][0][1], rectangles[i][1][0] - rectangles[i][0][0], rectangles[i][1][1] - rectangles[i][0][1]);
                this.octx.stroke();
            }
            var eclipses = this.eclipses;
            for (var i = 0; i < eclipses.length; i++) {
                var x = Math.min(eclipses[i][1][0], eclipses[i][0][0]);
                var y = Math.min(eclipses[i][1][1], eclipses[i][0][1]);
                var w = Math.abs(eclipses[i][1][0] - eclipses[i][0][0]);
                var h = Math.abs(eclipses[i][1][1] - eclipses[i][0][1]);
                var kappa = .5522848,
				ox = (w / 2) * kappa, // control point offset horizontal
		        oy = (h / 2) * kappa, // control point offset vertical
		        xe = x + w,           // x-end
		        ye = y + h,           // y-end
		        xm = x + w / 2,       // x-middle
		        ym = y + h / 2;       // y-middle

                this.octx.beginPath();
                this.octx.moveTo(x, ym);
                this.octx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
                this.octx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
                this.octx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
                this.octx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
                this.octx.closePath();
                this.octx.stroke();
            }
            var fibfans = this.fibfans;
            for (var i = 0; i < fibfans.length; i++) {
                var sX = fibfans[i][0][0], sY = fibfans[i][0][1], mX = fibfans[i][1][0], mY = fibfans[i][1][1];
                this.octx.beginPath();
                this.octx.moveTo(sX, sY);
                this.octx.lineTo(mX, mY);
                this.octx.lineWidth = 0.3;
                this.octx.stroke();
                this.octx.closePath();
                this.octx.beginPath();
                this.octx.lineWidth = 0.5;
                this.octx.font = "normal 10px Verdana";
                this.octx.fillStyle = "#FF0000";

                var h = Math.abs(mY - sY);
                var d = Math.abs(mX - sX);

                if (mX < sX && mY < sY) { // right to left and bottom to top
                    var tmpx = mX, tmpy = mY;
                    mX = sX; mY = sY;
                    sX = tmpx; sY = tmpy;
                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY + h * .382 * 2);
                    this.octx.fillText("61.8%", mX, sY + h * .382);

                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY + h * .5 * 2);
                    this.octx.fillText("50%", mX, sY + h * .5);

                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY + h * .618 * 2);
                    this.octx.fillText("38.2%", mX, sY + h * .618);
                    //                octx.moveTo(mX, mY);
                    //                octx.lineTo(sX, sY - h * .382);
                    //                octx.fillText("38.2%", sX, sY - h * .382);

                    //                octx.moveTo(mX, mY);
                    //                octx.lineTo(sX, sY - h * .5);
                    //                octx.fillText("50%", sX, sY - h * .5);

                    //                octx.moveTo(mX, mY);
                    //                octx.lineTo(sX + d, sY - h * .618);
                    //                octx.fillText("61.8%", sX, sY - h * .618);
                }
                else if (mX <= sX && mY >= sY) { // right to left and top to bottom
                    var tmpx = mX, tmpy = mY;
                    mX = sX; mY = sY;
                    sX = tmpx; sY = tmpy;
                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY - h * .382 * 2);
                    this.octx.fillText("61.8%", mX, sY - h * .382);

                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY - h * .5 * 2);
                    this.octx.fillText("50%", mX, sY - h * .5);

                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY - h * .618 * 2);
                    this.octx.fillText("38.2%", mX, sY - h * .618);
                    //                octx.moveTo(mX, mY);
                    //                octx.lineTo(sX , sY + h * .382 );
                    //                octx.fillText("38.2%", sX, sY + h * .382);

                    //                octx.moveTo(mX, mY);
                    //                octx.lineTo(sX , sY + h * .5 );
                    //                octx.fillText("50%", sX, sY + h * .5);

                    //                octx.moveTo(mX, mY);
                    //                octx.lineTo(sX , sY + h * .618 );
                    //                octx.fillText("61.8%", sX, sY + h * .618);
                }
                else if (mX >= sX && mY >= sY) { // left to right and top to bottom
                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY + h * .382 * 2);
                    this.octx.fillText("61.8%", mX, sY + h * .382);

                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY + h * .5 * 2);
                    this.octx.fillText("50%", mX, sY + h * .5);

                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY + h * .618 * 2);
                    this.octx.fillText("38.2%", mX, sY + h * .618);
                }
                else if (mX >= sX && mY < sY) { // left to right and bottom to top
                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY - h * .382 * 2);
                    this.octx.fillText("61.8%", mX, sY - h * .382);

                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY - h * .5 * 2);
                    this.octx.fillText("50%", mX, sY - h * .5);

                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY - h * .618 * 2);
                    this.octx.fillText("38.2%", mX, sY - h * .618);
                }


                this.octx.stroke();
                this.octx.closePath();
            }

            var fibrecs = this.fibrecs;

            for (var i = 0; i < fibrecs.length; i++) {
                var sX = fibrecs[i][0][0], sY = fibrecs[i][0][1], mX = fibrecs[i][1][0], mY = fibrecs[i][1][1];
                this.octx.beginPath();
                this.octx.moveTo(sX, sY);
                this.octx.lineTo(mX, mY);
                this.octx.lineWidth = 0.3;
                this.octx.stroke();
                this.octx.closePath();


                this.octx.beginPath();
                this.octx.lineWidth = 0.5;
                this.octx.font = "normal 10px Verdana";
                this.octx.fillStyle = "#FF0000";

                var h = Math.abs(mY - sY);
                var d = Math.abs(mX - sX);

                if (mX < sX && mY < sY) { // right to left and bottom to top 
                    this.octx.moveTo(mX, mY);
                    this.octx.lineTo(sX + d, mY);
                    this.octx.fillText("100%", sX, mY);

                    this.octx.moveTo(mX, sY);
                    this.octx.lineTo(sX + d, sY);
                    this.octx.fillText("0%", sX, sY);

                    this.octx.lineWidth = 0.3;
                    this.octx.moveTo(mX, mY + h / 2);
                    this.octx.lineTo(sX + d, mY + h / 2);
                    this.octx.fillText("50%", sX, mY + h / 2);

                    this.octx.moveTo(mX, mY + h * .382);
                    this.octx.lineTo(sX + d, mY + h * .382);
                    this.octx.fillText("61.8%", sX, mY + h * .382);

                    this.octx.moveTo(mX, mY + h * .618);
                    this.octx.lineTo(sX + d, mY + h * .618);
                    this.octx.fillText("38.2%", sX, mY + h * .618);
                }
                else if (mX <= sX && mY >= sY) { // right to left and top to bottom
                    this.octx.moveTo(mX, sY);
                    this.octx.lineTo(sX + d, sY);
                    this.octx.fillText("0%", sX, sY);

                    this.octx.moveTo(mX, mY);
                    this.octx.lineTo(sX + d, mY);
                    this.octx.fillText("100%", sX, mY);

                    this.octx.lineWidth = 0.3;
                    this.octx.moveTo(mX, sY + h / 2);
                    this.octx.lineTo(sX + d, sY + h / 2);
                    this.octx.fillText("50%", sX, sY + h / 2);

                    this.octx.moveTo(mX, sY + h * .382);
                    this.octx.lineTo(sX + d, sY + h * .382);
                    this.octx.fillText("38.2%", sX, sY + h * .382);

                    this.octx.moveTo(mX, sY + h * .618);
                    this.octx.lineTo(sX + d, sY + h * .618);
                    this.octx.fillText("61.8%", sX, sY + h * .618);
                }
                else if (mX >= sX && mY >= sY) { // left to right and top to bottom
                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY);
                    this.octx.fillText("100%", mX, sY);

                    this.octx.moveTo(sX, mY);
                    this.octx.lineTo(mX + d, mY);
                    this.octx.fillText("0%", mX, mY);

                    this.octx.lineWidth = 0.3;
                    this.octx.moveTo(sX, sY + h / 2);
                    this.octx.lineTo(mX + d, sY + h / 2);
                    this.octx.fillText("50%", mX, sY + h / 2);

                    this.octx.moveTo(sX, sY + h * .382);
                    this.octx.lineTo(mX + d, sY + h * .382);
                    this.octx.fillText("61.8%", mX, sY + h * .382);

                    this.octx.moveTo(sX, sY + h * .618);
                    this.octx.lineTo(mX + d, sY + h * .618);
                    this.octx.fillText("38.2%", mX, sY + h * .618);
                }
                else if (mX >= sX && mY < sY) { // left to right and bottom to top
                    this.octx.moveTo(sX, sY);
                    this.octx.lineTo(mX + d, sY);
                    this.octx.fillText("100%", mX, sY);

                    this.octx.moveTo(sX, mY);
                    this.octx.lineTo(mX + d, mY);
                    this.octx.fillText("0%", mX, mY);

                    this.octx.lineWidth = 0.3;
                    this.octx.moveTo(sX, mY + h / 2);
                    this.octx.lineTo(mX + d, mY + h / 2);
                    this.octx.fillText("50%", mX, mY + h / 2);

                    this.octx.moveTo(sX, mY + h * .382);
                    this.octx.lineTo(mX + d, mY + h * .382);
                    this.octx.fillText("38.2%", mX, mY + h * .382);

                    this.octx.moveTo(sX, mY + h * .618);
                    this.octx.lineTo(mX + d, mY + h * .618);
                    this.octx.fillText("61.8%", mX, mY + h * .618);
                }

                this.octx.stroke();
                this.octx.closePath();
            }

        },

        _initPlotCanvas: function () {
            // First determine the width and height. width won't change
            this.width = this.loffset + this.rmargin + this.plotwidth();
            //this.height = this.topmargin + this.plotht + this.loweriht + this.bmargin + (this.cp.numindicators * this.loweriht); //nhathn tinh chieu cao khung chua chart
            this.height = 405; //nhathn set height cua chart
            if (this.canvas) {
                // A canvas already exists, we probably need to resize the
                // canvas
                this.canvas.height = this.height;
                this.canvas.width = this.width;
                this.overlay.height = this.height;
                this.overlay.width = this.width;
            } else { // first time call to us
                this.canvas = _getCanvas(this.width, this.height);
                if (!this.canvas) {
                    throw "Cannot Initialize canvas";
                }
                this.canvas.plot = this;
                this.target.appendChild(this.canvas);

                this.overlay = _getCanvas(this.width, this.height);
                if (!this.overlay) {
                    throw "Cannot Initialize overlay";
                }
                this.overlay.style.position = 'absolute';
                // FIXME: Check if the code below is correct 
                //this.overlay.style.left = this._ElemPageOffsetX(this.canvas) + 'px'; //nhathn chinh lai canvas overlay
                //this.overlay.style.top = this._ElemPageOffsetY(this.canvas) + 'px';
                this.overlay.style.left = 0;
                this.overlay.style.top = 0;
                this.overlay.plot = this;
                this.overlay.tabIndex = 0;
                this.target.appendChild(this.overlay);
                this.removeELs().addELs();
            }
            // following we've to do everytime. 
            this.ctx = this.canvas.getContext("2d");
            this.ctx.fillStyle = this.cs.background;
            this.ctx.fillRect(0, 0, this.width, this.height);
            this.octx = this.overlay.getContext("2d");


            // We add copyright notice even before we've any data. lame.... :-)

            return this;
        },

        // Functions for adding and deleting indicators. We do some of our own validations

        addindicator: function (type, params) {

            // We don't assume that client has validated params. It's better to validate again
            if (!validParams(type, params)) return false;
            var success = true;
            if (this.supported.indexOf(type) == -1) {
                return false;
            }
            // max number of indicators we support .. 
            if (this.cp.numoverlays > 16) {  // default=7 with 'ema', 'sma', 'psar', 'bbands', 'rsi', 'stoch', 'macd'
                return false;
            }
            if (!this.current) {
                return false;
            }
            // check trung indicator da co thi ko add nua (ko tang infodiv)
            for (var j in this.current.overlays) {
                var strIndicator = this.current.overlays[j].strTitle;
                var re = /\d+|\,/;
                var re1 = /\w+/;
                var typeIndi = strIndicator.toLowerCase().match(re1);
                var paramIndi = /\(([^)]+)/.exec(strIndicator);
                if (paramIndi == null) {
                }
                else if (paramIndi[1]) {
                    paramIndi = paramIndi[1].split(/\s*,\s*/);
                }
                var inparam = '';
                for (var i in params)
                    inparam += params[i] + ',';
                inparam = inparam.replace(/,\s*$/, "");
                if (type == this.current.overlays[j].type && inparam == paramIndi) {

                    return false;
                }
            }
            for (var j in this.current.indicators) {
                var strIndicator = this.current.indicators[j].str;
                var re = /\d+|\,/;
                var re1 = /\w+/;
                var typeIndi = strIndicator.toLowerCase().match(re1);
                var paramIndi = /\(([^)]+)/.exec(strIndicator);
                if (paramIndi == null) {
                }
                else if (paramIndi[1]) {
                    paramIndi = paramIndi[1].split(/\s*,\s*/);
                }
                if (paramIndi == null) paramIndi = "";
                var inparam = '';
                for (var i in params)
                    inparam += params[i] + ',';
                inparam = inparam.replace(/,\s*$/, "");
                if (type == this.current.indicators[j].type && inparam == paramIndi) {

                    return false;
                }
            }

            // Some indicator specific checks go here . 
            switch (type) {
                case 'ema':
                case 'sma':
                    var period = parseInt(params[0]);
                    // NOTE: remove check here huynq 2014-09-11 move to check in drawIndiNew()
                    //                    if (period > 300) {
                    //                        period = 300;
                    //                    }
                    //                    else if (period < 2) {
                    //                        period = 2;
                    //                    }
                    if (type == 'ema') {
                        this.ema(this.current.ohlc, period);
                        infodiv += 17;
                    } else {
                        this.sma(this.current.ohlc, period);
                        infodiv += 17;
                    }
                    break;

                case 'psar':
                    var af = parseFloat(params[0]);
                    var maxaf = parseFloat(params[1]);

                    this.psar(this.current.ohlc, af, maxaf);
                    infodiv += 17;
                    break;
                case 'bbands':
                    var period = parseInt(params[0]);
                    var mult = (params[1]);

                    this.bbands(this.current.ohlc, period, mult);
                    infodiv += 51;
                    break;
                case 'macd':
                    var p1 = parseInt(params[0])
                    var p2 = parseInt(params[1])
                    var signal = parseInt(params[2])

                    this.macd(this.current.ohlc, p1, p2, signal);
                    infodiv += 34;
                    break;
                case 'rsi':
                    var lookback = parseInt(params[0]);

                    this.rsi(this.current.ohlc, lookback);
                    infodiv += 17;
                    break;
                case 'adx': // added adx nqhuy 14-05-2014
                    var lookback = parseInt(params[0]);

                    this.adx(this.current.ohlc, lookback);
                    infodiv += 51;
                    break;
                case 'stoch': // added huynq 21-05-2014
                    var k = parseInt(params[0]);
                    var x = parseInt(params[1]);
                    var d = parseInt(params[2]);

                    // FIXME : any validations? 
                    this.stoch(this.current.ohlc, k, x, d);
                    infodiv += 34;
                    break;
                case 'uo':
                    var fastavg = parseInt(params[0]);
                    var medavg = parseInt(params[1]);
                    var slowavg = parseInt(params[2]);


                    this.uo(this.current.ohlc, fastavg, medavg, slowavg);
                    infodiv += 17;

                    break;

                case 'momentum':
                    var lookback = parseInt(params[0]);

                    this.momentum(this.current.ohlc, lookback);
                    infodiv += 17;
                    break;
                case 'mfi':
                    var lookback = parseInt(params[0]);

                    this.mfi(this.current.ohlc, this.current.vol, lookback);
                    infodiv += 17;
                    break;
                case 'cmf':
                    var lookback = parseInt(params[0]);

                    this.cmf(this.current.ohlc, this.current.vol, lookback); //mod
                    infodiv += 17;
                    break;
                case 'perR':
                    var lookback = parseInt(params[0]);

                    this.perR(this.current.ohlc, lookback); //mod
                    infodiv += 17;
                    break;
                case 'obv':
                    this.obv(this.current.ohlc, this.current.vol);
                    infodiv += 17;
                    break;
                case 'atr':
                    var lookback = parseInt(params[0]);

                    this.atr(this.current.ohlc, lookback); //mod
                    infodiv += 17;
                    break;
                case 'cci':
                    var lookback = parseInt(params[0]);

                    this.cci(this.current.ohlc, lookback); //mod
                    infodiv += 17;
                    break;
                default:
                    break;
            }
            if (success) {
                this.plot();


            }
        },

        delindicator: function (which) {
            // added huynq 20-06-2014 delete bbands, psar, ema, sma tooltip
            for (var j in this.current.overlays) {
                if (which == this.current.overlays[j].strTitle) {
                    if (this.current.overlays[j].strTitle.substring(0, 6).toLowerCase() == 'bbands') {
                        infodiv -= 51;
                    }
                    else {
                        infodiv -= 17;
                    }
                    delete this.current.overlays[which];
                    this.cp.numoverlays -= 1;
                    //this.plot();                    // remark huynq 29-07-2014 PRO VER only

                    return;
                }
            }

            for (var j in this.current.indicators) {
                if (which == this.current.indicators[j].str) {
                    if (this.current.indicators[j].str.substring(0, 4).toLowerCase() == 'macd') {
                        infodiv -= 34;
                    }
                    else if (this.current.indicators[j].str.substring(0, 3).toLowerCase() == 'adx') {
                        infodiv -= 51;
                    }
                    else if (this.current.indicators[j].str.substring(0, 5).toLowerCase() == 'stoch') {
                        infodiv -= 34;
                    }
                    else if (this.current.indicators[j].str.substring(0, 5).toLowerCase() == 'bbands') {
                        infodiv -= 51;
                    }
                    else {
                        infodiv -= 17;
                    }


                    delete this.current.indicators[j];
                    this.cp.numindicators -= 1;





                    //this.plot();                  // remark huynq 29-07-2014 PRO VER only

                    return;
                }
            }

        },

        // sends the list of current indicators to the caller. Used in UI to Delete any indicators if not wanted
        getindicators: function () {
            var ilist = [];
            if (!this.current) {
                return ilist;
            }
            for (var j in this.current.overlays) {
                ilist.push(j);
            }
            for (var j in this.current.indicators) {
                ilist.push(this.current.indicators[j].str);
            }
            return ilist;
        },

        /* removeELs: function() {
        var o = this.overlay;

            //bruteforce... remove everything
        if(o.attachEvent) {
        o.detachEvent('onmousedown', _beginTrendLine);
        o.detachEvent('onmousemove', _drawTrendLine);
        o.detachEvent('onmouseup', _endTrendLine);
        o.detachEvent('onkeyup', _keyActions);
        o.detachEvent('onmousedown', _beginPanning(e););
        //o.detachEvent('onmousedown', function() { return _beginPanning(e);} );
        o.detachEvent('onmousemove', _doPanning);
        o.detachEvent('pnmouseup', _endPanning);
        } else {
        o.removeEventListener('mousedown', _beginTrendLine, false);
        o.removeEventListener('mousemove', _drawTrendLine, false);
        o.removeEventListener('mouseup', _endTrendLine, false);
        o.removeEventListener('keyup', _keyActions, false);
        o.removeEventListener('mousedown', _beginPanning, false);
        o.removeEventListener('mousemove', _doPanning, false);
        o.removeEventListener('mouseup', _endPanning, false);
        }
        // returns self to the caller to allow chaining
        return this;

        },

        addELs: function() {
        var o = this.overlay;
        if (!this.mode) {
        if(o.attachEvent) {
        o.attachEvent('onmousedown', _beginTrendLine);
        o.attachEvent('onmousemove', _drawTrendLine);
        o.attachEvent('onmousemove', _endTrendLine);
        } else {
        o.addEventListener('mousedown', _beginTrendLine, false);
        o.addEventListener('mousemove', _drawTrendLine, false);
        o.addEventListener('mouseup', _endTrendLine, false);
        }
        } else { // Pan mode 
        if(o.attachEvent) {
        o.attachEvent('onmousedown', _beginPanning);
        //o.attachEvent('onmousedown', function() { return _beginPanning(e); });
        o.attachEvent('onmousemove', _doPanning);
        o.attachEvent('onmouseup', _endPanning);
        } else {
        o.addEventListener('mousedown', _beginPanning, false);
        o.addEventListener('mousemove', _doPanning, false);
        o.addEventListener('mouseup', _endPanning, false);
        }
        }
        if(o.attachEvent) {
        o.attachEvent('onkeyup', this._keyActions);
        } else {
        o.addEventListener('keyup', _keyActions, false);
        }
        return this.overlay;
        }, */

        // removes all the event listners on the overlay. 

        removeELs: function () {
            var o = this.overlay;

            //bruteforce... remove everything
            if (o.attachEvent) {
                o.detachEvent('onmousemove', _showTooltip);

                o.detachEvent('onmousedown', _beginTrendLine);
                o.detachEvent('onmousemove', _drawTrendLine);
                o.detachEvent('onmouseup', _endTrendLine);

                o.detachEvent('onmousedown', _beginRectangle);
                o.detachEvent('onmousemove', _drawRectangle);
                o.detachEvent('onmouseup', _endRectangle);

                o.detachEvent('onmousedown', _beginEclipse);
                o.detachEvent('onmousemove', _drawEclipse);
                o.detachEvent('onmouseup', _endEclipse);

                o.detachEvent('onmousedown', _beginFibFan);
                o.detachEvent('onmousemove', _drawFibFan);
                o.detachEvent('onmouseup', _endFibFan);

                o.detachEvent('onmousedown', _beginFibRec);
                o.detachEvent('onmousemove', _drawFibRec);
                o.detachEvent('onmouseup', _endFibRec);

                o.detachEvent('onkeyup', _keyActions);
                o.detachEvent('onmousedown', _beginPanning);
                o.detachEvent('onmousemove', _doPanning);
                o.detachEvent('onmouseup', _endPanning);
            } else {
                o.removeEventListener('mousemove', _showTooltip, false);

                o.removeEventListener('mousedown', _beginTrendLine, false);
                o.removeEventListener('mousemove', _drawTrendLine, false);
                o.removeEventListener('mouseup', _endTrendLine, false);

                o.removeEventListener('mousedown', _beginRectangle, false);
                o.removeEventListener('mousemove', _drawRectangle, false);
                o.removeEventListener('mouseup', _endRectangle, false);

                o.removeEventListener('mousedown', _beginEclipse, false);
                o.removeEventListener('mousemove', _drawEclipse, false);
                o.removeEventListener('mouseup', _endEclipse, false);

                o.removeEventListener('mousedown', _beginFibFan, false);
                o.removeEventListener('mousemove', _drawFibFan, false);
                o.removeEventListener('mouseup', _endFibFan, false);

                o.removeEventListener('mousedown', _beginFibRec, false);
                o.removeEventListener('mousemove', _drawFibRec, false);
                o.removeEventListener('mouseup', _endFibRec, false);

                o.removeEventListener('keyup', _keyActions, false);
                o.removeEventListener('mousedown', _beginPanning, false);
                o.removeEventListener('mousemove', _doPanning, false);
                o.removeEventListener('mouseup', _endPanning, false);
            }


            // returns self to the caller to allow chaining
            return this;

        },

        removeELshapes: function () {
            var o = this.overlay;

            //bruteforce... remove everything
            if (o.attachEvent) {
                o.detachEvent('onmousemove', _showTooltip);

                o.detachEvent('onmousedown', _beginTrendLine);
                o.detachEvent('onmousemove', _drawTrendLine);
                o.detachEvent('onmouseup', _endTrendLine);

                o.detachEvent('onmousedown', _beginRectangle);
                o.detachEvent('onmousemove', _drawRectangle);
                o.detachEvent('onmouseup', _endRectangle);

                o.detachEvent('onmousedown', _beginEclipse);
                o.detachEvent('onmousemove', _drawEclipse);
                o.detachEvent('onmouseup', _endEclipse);

                o.detachEvent('onmousedown', _beginFibFan);
                o.detachEvent('onmousemove', _drawFibFan);
                o.detachEvent('onmouseup', _endFibFan);

                o.detachEvent('onmousedown', _beginFibRec);
                o.detachEvent('onmousemove', _drawFibRec);
                o.detachEvent('onmouseup', _endFibRec);
            }
            else {
                o.removeEventListener('mousemove', _showTooltip, false);

                o.removeEventListener('mousedown', _beginTrendLine, false);
                o.removeEventListener('mousemove', _drawTrendLine, false);
                o.removeEventListener('mouseup', _endTrendLine, false);

                o.removeEventListener('mousedown', _beginRectangle, false);
                o.removeEventListener('mousemove', _drawRectangle, false);
                o.removeEventListener('mouseup', _endRectangle, false);

                o.removeEventListener('mousedown', _beginEclipse, false);
                o.removeEventListener('mousemove', _drawEclipse, false);
                o.removeEventListener('mouseup', _endEclipse, false);

                o.removeEventListener('mousedown', _beginFibFan, false);
                o.removeEventListener('mousemove', _drawFibFan, false);
                o.removeEventListener('mouseup', _endFibFan, false);

                o.removeEventListener('mousedown', _beginFibRec, false);
                o.removeEventListener('mousemove', _drawFibRec, false);
                o.removeEventListener('mouseup', _endFibRec, false);
            }
            // returns self to the caller to allow chaining
            return this;
        },

        addELs: function () {

            if (this.mode == 1) { // Pan mode
                if (this.overlay.attachEvent) {
                    this.overlay.attachEvent('onmousedown', _beginPanning);
                    this.overlay.attachEvent('onmousemove', _doPanning);
                    this.overlay.attachEvent('onmouseup', _endPanning);
                    this.overlay.attachEvent('onkeyup', _keyActions);
                } else {
                    this.overlay.addEventListener('mousedown', _beginPanning, false);
                    this.overlay.addEventListener('mousemove', _doPanning, false);
                    this.overlay.addEventListener('mouseup', _endPanning, false);
                    this.overlay.addEventListener('keyup', _keyActions, false);
                }
            }

            return this.overlay;

        },

        //added huynq 06-06-2014
        draw: function (item) {
            var o = this.overlay;
            if (item == 0) {
                itemdraw = 0;

                this.removeELshapes();

                if (o.attachEvent) o.attachEvent('onmousemove', _showTooltip);
                else
                    o.addEventListener('mousemove', _showTooltip, false);
            }
            if (item == 1) {
                itemdraw = 1;

                this.removeELshapes();

                o.addEventListener('mousedown', _beginTrendLine, false);
                o.addEventListener('mousemove', _drawTrendLine, false);
                o.addEventListener('mouseup', _endTrendLine, false);
            }
            if (item == 2) {
                itemdraw = 2;
                this.removeELshapes();

                o.addEventListener('mousedown', _beginRectangle, false);
                o.addEventListener('mousemove', _drawRectangle, false);
                o.addEventListener('mouseup', _endRectangle, false);
            }
            if (item == 3) {
                itemdraw = 3;
                this.removeELshapes();

                o.addEventListener('mousedown', _beginEclipse, false);
                o.addEventListener('mousemove', _drawEclipse, false);
                o.addEventListener('mouseup', _endEclipse, false);
            }
            if (item == 4) {
                itemdraw = 4;
                this.removeELshapes();

                o.addEventListener('mousedown', _beginFibFan, false);
                o.addEventListener('mousemove', _drawFibFan, false);
                o.addEventListener('mouseup', _endFibFan, false);
            }
            if (item == 5) {
                itemdraw = 5;
                this.removeELshapes();

                o.addEventListener('mousedown', _beginFibRec, false);
                o.addEventListener('mousemove', _drawFibRec, false);
                o.addEventListener('mouseup', _endFibRec, false);
            }
        },

        undo: function () {
            _undo();
        },

        // modified huynq 06-06-2014
        changemode: function (mode) {
            mode = parseInt(mode);
            if (isNaN(mode)) {
                return false;
            }
            //if(mode && mode != 1) { 
            //    return false;
            //} 
            if (this.mode === mode) {
                return false; // don't do anything if mode is same 
            }
            this.mode = mode;
            this.removeELs().addELs();
            if (mode == 0) return false;
            this.lines = [], this.rectangles = [], this.fibfans = [], this.fibrecs = [],
            this.eclipses = [], this.shapes = [], this.lastitem = [];
            if (itemdraw == 1) { this.draw(1); }
            if (itemdraw == 2) { this.draw(2); }
            if (itemdraw == 3) { this.draw(3); }
            if (itemdraw == 4) { this.draw(4); }
            if (itemdraw == 5) { this.draw(5); }
            this.plot();

            // added huynq 30-05-2014
            var divObj = document.getElementById("lineTooltip");
            var divObjX = document.getElementById("lineTooltipX"); //nhathn them duong line ox
            if (mode != 1) {
                divObj.style.display = 'none';
                divObjX.style.display = 'none';
                var s = this.infodiv.style;
                s.display = 'none';
            }
            else if (mode == 1) {
                divObj.style.display = 'block';
                divObjX.style.display = 'block';
                this.removeELs().addELs();
            }
        },
        plot: function () {
            /* 
            * first try to plot the data
            */

            this._initPlotCanvas();
            var all = this.getindicators();
            this._doplot(this.ctx, this.current);
            //if (!this.mode) { 
            //    this.drawlines();
            //} 
            if (itemdraw == 1) { this.draw(1); }
            if (itemdraw == 2) { this.draw(2); }
            if (itemdraw == 3) { this.draw(3); }
            if (itemdraw == 4) { this.draw(4); }
            if (itemdraw == 5) { this.draw(5); }
        },

        plotempty: function () {
            this.cp = { numindicators: 0 };
            this._initPlotCanvas();
            this._drawText("No data found....", 100, 100, { font: '20pt Arial' });

        },

        plotloading: function () {
            this.cp = { numindicators: 0 };
            this._initPlotCanvas();
            this._drawText("Loading....", 100, 100, { font: '20pt Arial' });

        },
        /* low level plot function. Should not be used directly. */
        /* Since this is a plotting function, we don't do any calculation 
        inside this function. We assume, all data is with us
        ctx : the context on which to plot.
        dataset : complete dataset - OHLC plus overlays plus lower 
        indicators if any 
        shift : if present, specifies , shift relative to last plot, for
        the first plot, makes no sense, used in pan mode. 
        */
        _doplot: function (ctx, dataset, shift) {

            var cs = this.cs;
            var cp = this.cp;

            var ts = dataset.ts;
            var data = dataset.ohlc;
            var vol = dataset.vol;
            var overlays = dataset.overlays;
            var indicators = dataset.indicators;

            /* let's clear ourselves before we start plotting anything */
            this._clear(ctx);

            var ob = this._window(data, overlays, shift);
            var xmin = ob.xmin, ymin = ob.ymin, xmax = ob.xmax, ymax = ob.ymax;
            //var xmin = this.zoomtype(2, ts).xmin;

            // We get top, bottom, right, left of the plot and draw a bounding 
            // rectangle 
            var _top = this.topmargin, _left = this.loffset, _right = _left + this.plotwidth(), _bottom = _top + this.plotht;
            ctx.strokeStyle = this.cs.stroke;
            ctx.lineWidth = .5;
            if (screen.width >= 1300)
                ctx.strokeRect(_left - 5 - .5, _top - .5, this.plotwidth() + 5, this.plotht);
            else if (screen.width >= 1100)
                ctx.strokeRect(_left - 5 + .2, _top + .2, this.plotwidth() + 5, this.plotht + .2);
            else
                ctx.strokeRect(_left - 5 + .3, _top + .3, this.plotwidth() + 5, this.plotht);

            var h = _bottom;

            // If the scale is log scale, we use, Math.log and Math.exp or else
            // we use identity function.
            // Max candles we'd be plotting would not be more than hundred or
            // so, hence calculating log and exp is not as expensive as it may
            // appear than doing it for entire data. 
            var _log = (cp.logscale ? Math.log : function (x) { return x });
            var _exp = (cp.logscale ? Math.exp : function (x) { return x });
            var c = Math.round(cp.cwidth / 2); // center of first (and every) cdl 
            var csize = cp.cwidth / 1.6;  // modified huynq 30-07-2014 display full volume size remove Math.round

            // the following variables are needed for plotting volume.
            var vt = this.topmargin + this.plotht + this.vspacing;
            var vb = vt + this.liplotht + this.limargin;
            var vl = this.loffset;
            var vr = this.loffset + this.plotwidth();
            var vymax = _minmax1d(vol.slice(xmin, xmax))[1] * 1.1;
            var vymin = 0;
            var vrange = vymax - vymin;
            var vscale = this.liplotht / vrange;
            var vh = vb;
            ctx.strokeStyle = this.cs.stroke;
            ctx.lineWidth = 0.5;
            //if (screen.width >= 1300) nhathn an khung volume
            //    ctx.strokeRect(vl - 5 - .5, vt + 12 - .5, this.plotwidth() + 5, this.liplotht + this.limargin - 12); // vt to vt+12 (display date below price chart)
            //else if (screen.width >= 1100)
            //    ctx.strokeRect(vl - 5 + .2, vt + 12 + .2, this.plotwidth() + 5, this.liplotht + this.limargin - 12 + .2);
            //else
            //    ctx.strokeRect(vl - 5 + .3, vt + 12 + .3, this.plotwidth() + 5, this.liplotht + this.limargin - 12);

            var range = ymax - ymin;
            var scale = (range == 0) ? (this.plotht / 0.1) : (this.plotht / range); // scale: how much a unit takes on the plot
            cp.ymin = (range == 0) ? cp.ymin - 0.1 : cp.ymin;
            var prevxy = []; // used for drawing line to last point


            var prevC = (xmin == 0) ? ((_log(data[0][3]) - cp.ymin) * scale) : ((_log(data[xmin - 1][3]) - cp.ymin) * scale);
            var yprevline = (xmin == 0) ? ((_log(data[0][0]) - cp.ymin) * scale) : ((_log(data[xmin - 1][0]) - cp.ymin) * scale);
            for (var i = xmin; i < xmax; i++) { // xmax: so luong du lieu max
                // fix if data wrong ex high<open, low>open
                data[i][0] = data[i][0] > 1.5 * data[i][3] ? data[i][3] : data[i][0];
                data[i][1] = data[i][1] <= data[i][0] ? data[i][0] : data[i][1];
                data[i][2] = data[i][2] >= data[i][0] ? data[i][0] : data[i][2];

                var yop = ((_log(data[i][0]) - cp.ymin) * scale);
                var yhi = ((_log(data[i][1]) - cp.ymin) * scale);
                var ylo = ((_log(data[i][2]) - cp.ymin) * scale);
                var ycl = ((_log(data[i][3]) - cp.ymin) * scale);
                var pdate = ts[i];
                var popen = ((_log(data[i][0]) - cp.ymin) * scale);
                var phigh = ((_log(data[i][1]) - cp.ymin) * scale);
                var plow = ((_log(data[i][2]) - cp.ymin) * scale);
                var pclose = ((_log(data[i][3]) - cp.ymin) * scale);

                var xlo = (c + (i - xmin) * cp.cwidth) - csize + this.loffset;
                var xline = xlo + Math.round(csize / 2);

                /* invert colors if Open > Close */
                // FIXME : Fix for Opera.. Doesn't like negative width/height
                // FIXME : check if it works
                var color;
                if (popen > pclose) {    // open>close
                    color = cs.rcandle; // red candle
                } else {
                    color = cs.gcandle; // green candle
                }
                if (cp.type == 5) {  // candle-stick meta
                    if (ycl > yop) {
                        if (ycl > prevC) {
                            ctx.strokeStyle = cs.gcandle;
                            ctx.lineWidth = 1.5;
                            //ctx.fillStyle=cs.bodycandle;
                        }
                        else {
                            ctx.strokeStyle = cs.rcandle;
                            ctx.lineWidth = 1.5;
                            //ctx.fillStyle=cs.bodycandle;
                        }
                    }
                    else {
                        if (ycl > prevC) {
                            ctx.fillStyle = cs.gcandle;
                        }
                        else {
                            ctx.fillStyle = cs.rcandle;

                        }
                    }

                    ctx.lineWidth = 1.5;
                    if (ycl > yop) {
                        ctx.strokeRect(xlo, h - yop, csize, yop - ycl);
                        _drawline(ctx, xline, h - yhi, xline, h - ylo, ctx.strokeStyle, 1.5);
                        ctx.fillStyle = cs.bodycandle;

                        ctx.fillRect(xlo, h - yop, csize, yop - ycl);
                    }
                    else {
                        ctx.fillRect(xlo, h - yop, csize, yop - ycl);
                        _drawline(ctx, xline, h - yhi, xline, h - ylo, ctx.fillStyle, 1.5);
                    }

                    if (!(yop - ycl)) {
                        ctx.fillRect(xlo, h - yop, csize, 1.5);
                    }

                }
                else if (cp.type == 2) { // OHLC 
                    // add color correct
                    if (yop > ycl) {
                        ctx.fillStyle = cs.rcandle;
                    } else {
                        ctx.fillStyle = cs.gcandle;
                    }
                    _drawline(ctx, xline, h - yhi, xline, h - ylo, ctx.fillStyle, 1.5);
                    _drawline(ctx, xlo, h - yop, xline, h - yop, ctx.fillStyle, 1.5);
                    _drawline(ctx, xline, h - ycl, xlo + csize, h - ycl, ctx.fillStyle, 1.5);
                } else if (cp.type == 3) { // Line  
                    if (i - xmin > 0) { /* skip first line */
                        _drawline(ctx, prevxy[0], prevxy[1], xline, h - ycl, cs.stroke, 1);
                    }
                    prevxy = [xline, h - ycl];
                }
                else if (cp.type == 4) {  // Heikin-Ashi
                    // add color correct
                    if (yop > ycl) {
                        ctx.fillStyle = cs.rcandle;
                    } else {
                        ctx.fillStyle = cs.gcandle;
                    }
                    ctx.fillRect(xlo, h - yop, csize, yop - ycl);
                    if (!(yop - ycl)) {
                        ctx.fillRect(xlo, h - yop, csize, 1);
                    }
                    _drawline(ctx, xline, h - yhi, xline, h - ylo, ctx.fillStyle, 1);
                }
                else if (cp.type == 1) {
                    if (yop > ycl) {
                        ctx.fillStyle = cs.rcandle;
                    } else {
                        ctx.fillStyle = cs.gcandle;
                    }
                    ctx.fillRect(xlo, h - yop, csize, yop - ycl);
                    if (!(yop - ycl)) {
                        ctx.fillRect(xlo, h - yop, csize, 1.5);
                    }
                    if (cp.zt == 4)
                        _drawline(ctx, xline, h - yhi, xline, h - ylo, ctx.fillStyle, 0.3);
                    else if (cp.zt == 3)
                        _drawline(ctx, xline, h - yhi, xline, h - ylo, ctx.fillStyle, 0.7);
                    else
                        _drawline(ctx, xline, h - yhi, xline, h - ylo, ctx.fillStyle, 1.5);
                }
                /* try plotting the volume */
                if (vol[i]) {
                    var yvol = vol[i] * vscale;
                    if (cp.type == 5) {
                        if (ycl > yop) {
                            if (ycl > prevC) {
                                ctx.fillStyle = cs.gcandle;
                                ctx.lineWidth = 1;
                                // ctx.fillRect(xlo, vh - yvol, csize, yvol);
                            }
                            else {
                                ctx.fillStyle = cs.rcandle;
                                ctx.lineWidth = 1;
                                //  ctx.fillRect(xlo, vh - yvol, csize, yvol);
                            }
                        }
                        else {
                            if (ycl > prevC) {
                                ctx.fillStyle = cs.gcandle;
                                // ctx.fillRect(xlo, vh - yvol, csize, yvol);
                            }
                            else {
                                ctx.fillStyle = cs.rcandle;
                                //  ctx.fillRect(xlo, vh - yvol, csize, yvol);
                            }
                        }


                        ctx.fillRect(xlo, vh - yvol, csize, yvol);

                        if (!(yop - ycl)) {
                            //  ctx.fillRect(xlo, vh - yvol, csize, 1);
                        }

                    }
                    else {

                        if (yop > ycl) {
                            ctx.fillStyle = cs.rcandle;
                        } else {
                            ctx.fillStyle = cs.gcandle;
                            var t = ycl; ycl = yop; yop = t;
                        }
                        // FIXME : Fix for opera, check if it works 
                        //ctx.fillRect(xlo, vh - yvol, csize, yvol);
                    }

                }

                prevC = ((_log(data[i][3]) - cp.ymin) * scale);
            };

            /* plot any overlay indicators */
            var k = 0;
            var dataoverlayss = [];
            var titleoverlay = "";
            for (var o in overlays) {
                // TODO: evaluate, whether it makes sense to have separate 
                // functions for each of the overlays. Right now this looks ok. 
                var prevxy = [];
                var o1 = overlays[o].data;
                for (var j = xmin; j < xmax; j++) {
                    var i = j - xmin;
                    var ycl = Math.round((_log(o1[j]) - ymin) * scale);
                    if (!o.search('psar')) { //overlay name begins with psar.. this is our
                        var pwidth = cp.minwidth / 8;


                        var xlo = (c + i * cp.cwidth) - 3 * pwidth + this.loffset;

                        ctx.fillStyle = cs.psar;
                        //FIXME: Fix for opera, check

                        if (pwidth >= 2) pwidth = 2
                        else if (pwidth < 0.5) pwidth = 0.5;

                        ctx.fillRect(xlo, h - ycl - 2 * pwidth, 2 * pwidth, 2 * pwidth);

                    } else if (!o.search('bbands')) {
                        if (!o1[j][0]) {
                            continue;
                        }
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);
                        var bmax = Math.round((_log(o1[j][0]) - ymin) * scale);
                        var bm = Math.round((_log(o1[j][1]) - ymin) * scale);
                        var bmin = Math.round((_log(o1[j][2]) - ymin) * scale);
                        if (i > 0 && prevxy[0]) { //skip first line
                            _drawline(ctx, prevxy[0][0], prevxy[0][1], xline, h - bmax, cs.bbands[0], 0.4);
                            _drawline(ctx, prevxy[1][0], prevxy[1][1], xline, h - bm, cs.bbands[1], 1);
                            _drawline(ctx, prevxy[2][0], prevxy[2][1], xline, h - bmin, cs.bbands[2], 0.4);
                        }
                        prevxy = [[xline, h - bmax], [xline, h - bm], [xline, h - bmin]];

                    } else {

                        overlays[o].offset = k;
                        if (!o1[j]) {
                            continue;
                        }
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        if (i > 0 && prevxy[0]) { //skip first line
                            _drawline(ctx, prevxy[0], prevxy[1], xline, h - ycl, cs.overlays[k % cs.overlays.length], 0.5); //nhathn set chieu cao cua duong sma
                        }
                        prevxy = [xline, h - ycl];
                    }
                    if (!o.search('bbands')) {
                        var bb = overlays[o].tooltipbbmid[j];
                        var bbtop = overlays[o].tooltipbbtop[j];
                        var bbbot = overlays[o].tooltipbbbot[j];
                    }
                    else {
                        var datama = overlays[o].data[j];
                    }

                }
                k += 1;
                //if (overlays[o].offset !== undefined) {
                //    var z = overlays[o].offset;
                //    if (!o.search('bbands')) {
                //        //this._drawLegend('PSAR = ' + Math.round(datama * 100) / 100, z);
                //    }
                //    else if (!o.search('psar')) {
                //        this._drawLegend('PSAR = ' + Math.round(datama * 100) / 100, z);
                //    }
                //    else
                //        this._drawLegend(o + ' = ' + Math.round(datama * 100) / 100, z);
                //}
                var color = [];

                if (overlays[o].offset !== undefined) {
                    if (!o.search('bbands')) {
                        titleoverlay += overlays[o].str.replace('BBAND', 'BBTop') + "= " + Math.round(bbtop * 100) / 100 + ", ";
                        titleoverlay += overlays[o].str.replace('BBAND', 'BBMid') + "= " + Math.round(bb * 100) / 100 + ", ";
                        titleoverlay += overlays[o].str.replace('BBAND', 'BBBot') + "= " + Math.round(bbbot * 100) / 100 + ", ";
                    }
                    else {
                        titleoverlay += overlays[o].str + "= " + Math.round(datama * 100) / 100 + ", ";
                    }
                }
            }

            this._drawLegend(titleoverlay.replace(/,(?=[^,]*$)/, ''), 0); // remove comma at end


            var e = document.getElementById("selectts");
            var strTimeVal = e.options[e.selectedIndex].value;

            /* and X and Y axis  FIXME:  get it right */
            var ystops;
            if (ymax > 100) { ystops = this._ygrid(_exp(ymin), _exp(ymax), cp.maxylabels); }
            else { ystops = this._ygrid(_exp(ymin), _exp(ymax), 8); }

            for (var i in ystops) { //nhathn bang chia gia tri cua candlestick
                var logystp = _log(ystops[i]);
                var y1 = Math.round((logystp - cp.ymin) * scale);

                if (y1 > 20) { // don't draw anything in first 20 pixels
                    _drawDashedLine(ctx, _left, h - y1, _right, h - y1, cs.stroke, 0.14);   // ve duong ke ngang Price
                    _drawline(ctx, _right, h - y1, _right + 4, h - y1, cs.stroke, 0.5); // thang chia do
                    label = Math.round(ystops[i] * 10) / 10;
                    this._drawText(label, _right, h - y1 + 3, { align: 'left', padding: 5, font: '7pt Arial' }); // label duong ke ngang Price
                }
            };
            //if(strTimeVal>0)
            //	_drawline(ctx, _left , h - yprevline, _right, h - yprevline, cs.rcandle, 0.5); // duong ke gia close hom truoc
            // add label close (right panel) with color
            var closeCurrent = [pclose / scale + cp.ymin];
            for (var i in closeCurrent) {
                var y1 = (_log(closeCurrent[i]) - cp.ymin) * scale;
                label = Math.round(closeCurrent[i] * 100) / 100;
                //nhathn an gia tri sam 
                //this._drawText(label, _right, h - y1, { align: 'left', padding: 5, color: color, font: '8pt Arial' }); // label Price
            }


            var volstops = [];
            var volstops = this._vygrid(vymin, vymax, 3); //new Array(2);
            //for (var i = 0; i < 2; i++) {
            //    var j = i + 1;
            //    volstops[i] = parseInt(format_number(Math.round(_exp(vymax)) * (j) / 2, -5));
            //}
            //an bang chi do volume
            //for (var i in volstops) {
            //    if (volstops[i] == 0) continue;
            //    var vollabels = this._volgrid(volstops[i], 1);
            //    var y1 = Math.round(volstops[i] * vscale);
            //    _drawDashedLine(ctx, _left, vh - y1, _right, vh - y1, cs.stroke, 0.14);    // ve duong ke ngang volume
            //    _drawline(ctx, _right, vh - y1, _right + 4, vh - y1, cs.stroke, 0.5); // thang chia do
            //    //label = numberWithCommas(volstops[i]);

            //    this._drawText(vollabels, _right, vh - y1, { align: 'right', padding: 70, font: '8pt Arial' }); // label duong ke ngang Volume
            //}
            var volCurrent = [yvol / vscale];
            for (var i in volCurrent) {
                var y1 = Math.round(volCurrent[i] * vscale);
                label = numberWithCommas(Math.round(volCurrent[i] * 10) / 10);
                //nhathn an volume o MACD
                //this._drawText(label, _right, vh - y1, { align: 'left', padding: 5, color: color, font: '8pt Arial' }); // label Volume
            }

            h = h + (cp.numindicators + 1) * this.loweriht;

            var pwd = 7;
            if (strTimeVal == 0) {  // daily chart
                if (xmax - xmin > 600) {  // year
                    var j = -1, yearDarr = [], xminarr = [];
                    for (var i = xmin; i < xmax; i++) {
                        var dateStr = ts[i]; //use code for IE
                        if (dateStr.length > 10) {
                            var a = dateStr.split(" ");
                            var d1 = a[0].split("-");
                            var t = a[1].split(":");
                            var d = new Date(d1[0], (d1[1] - 1), d1[2], t[0], t[1]);
                        }
                        else {
                            var d = new Date(ts[i]);
                        }

                        //var d = new Date(ts[i]);
                        var wom = d.getDate();

                        var year = d.getFullYear();
                        var curryear;

                        if (wom < 32 && year != curryear) {
                            j++;
                            yearDarr[j] = ts[i];
                            xminarr[j] = i;
                            curryear = _tsToDate(ts[i]).substring(6, 10);
                        }
                    }
                    //for (var i = 0; i < yearDarr.length; i++) { //nhathn an nam cho chart 4 nam
                    //    var label = _tsToDate(yearDarr[i]).substring(6, 10);
                    //    var xlo = (c + (xminarr[i] - xmin) * cp.cwidth) - csize + this.loffset;
                    //    var xline = xlo + Math.round(csize / 2);
                    //    if (xlo > this.loffset + 20) {
                    //        _drawDashedLine(ctx, xline, h + 12, xline, this.topmargin, cs.stroke, 0.14); // ve duong ke doc // xline+3 added 17-06-2014 // h+12 chen date len tren
                    //        ctx.fillStyle = cs.label;
                    //        ctx.textAlign = "center";
                    //        ctx.fillText(label, xlo, vt + 8) // h + this.vspacing + 10); to vt+8 (display date below price chart)
                    //    }
                    //}
                }
                else if (xmax - xmin > 270) { // quater
                    var j = -1, quarterarr = [], xminarr = [];
                    for (var i = xmin; i < xmax; i++) {
                        var dateStr = ts[i]; //use code for IE
                        if (dateStr.length > 10) {
                            a = dateStr.split(" ");
                            var d1 = a[0].split("-");
                            var t = a[1].split(":");
                            var d = new Date(d1[0], (d1[1] - 1), d1[2], t[0], t[1]);
                        }
                        else {
                            var d = new Date(ts[i]);
                        }

                        //var d = new Date(ts[i]);
                        var wom = d.getDate();
                        var mon = d.getMonth() + 1;
                        var quarter = Math.floor((mon - 1 + 3) / 3);
                        var currquarter;

                        if (wom < 32 && quarter != currquarter) {
                            j++;
                            quarterarr[j] = ts[i];
                            xminarr[j] = i;
                            currquarter = Math.floor((parseInt(_tsToDate(ts[i]).substring(3, 5) - 1) + 3) / 3);
                        }
                    }
                    //for (var i = 0; i < quarterarr.length; i++) {
                    //    var label = _tsToDate(quarterarr[i]).substring(3, 10);
                    //    var xlo = (c + (xminarr[i] - xmin) * cp.cwidth) - csize + this.loffset;
                    //    var xline = xlo + Math.round(csize / 2);
                    //    if (xlo > this.loffset + 20) {
                    //        _drawDashedLine(ctx, xline, h + 12, xline, this.topmargin, cs.stroke, 0.14); // ve duong ke doc // xline+3 added 17-06-2014 // h+12 chen date len tren
                    //        ctx.fillStyle = cs.label;
                    //        ctx.textAlign = "center";
                    //        ctx.fillText(label, xlo, vt + 8) // h + this.vspacing + 10); to vt+8 (display date below price chart)
                    //    }
                    //}
                }
                else if (xmax - xmin < 70) { // month
                    var j = -1, montharr = [], xminarr = [];
                    for (var i = xmin; i < xmax; i++) {
                        var dateStr = ts[i]; //use code for IE
                        if (dateStr.length > 10) {
                            a = dateStr.split(" ");
                            var d1 = a[0].split("-");
                            var t = a[1].split(":");
                            var d = new Date(d1[0], (d1[1] - 1), d1[2], t[0], t[1]);
                        }
                        else {
                            var d = new Date(ts[i]);
                        }

                        //var d = new Date(ts[i]);
                        var wom = d.getDay();

                        if (wom < pwd) {
                            j++;
                            montharr[j] = ts[i];
                            xminarr[j] = i;

                        }
                        pwd = wom;

                    }
                    //for (var i = 0; i < montharr.length; i++) {
                    //    var label = _tsToDate(montharr[i]).substring(0, 10);
                    //    var xlo = (c + (xminarr[i] - xmin) * cp.cwidth) - csize + this.loffset;
                    //    var xline = xlo + Math.round(csize / 2);
                    //    if (xlo > this.loffset + 20) {
                    //        _drawDashedLine(ctx, xline, h + 12, xline, this.topmargin, cs.stroke, 0.14); // ve duong ke doc // h+12 chen date len tren
                    //        ctx.fillStyle = cs.label;
                    //        ctx.textAlign = "center";
                    //        ctx.fillText(label, xlo, vt + 8) // h + this.vspacing + 10); to vt+8 (display date below price chart)
                    //    }
                    //}
                }
                else {
                    var j = -1, montharr = [], xminarr = [];
                    for (var i = xmin; i < xmax; i++) {
                        var dateStr = ts[i]; //use code for IE
                        if (dateStr.length > 10) {
                            a = dateStr.split(" ");
                            var d1 = a[0].split("-");
                            var t = a[1].split(":");
                            var d = new Date(d1[0], (d1[1] - 1), d1[2], t[0], t[1]);
                        }
                        else {
                            var d = new Date(ts[i]);
                        }

                        //var d = new Date(ts[i]);
                        var wom = d.getDate();
                        var mon = d.getMonth() + 1;
                        var currmon;

                        if (wom < 32 && mon != currmon) {
                            j++;
                            montharr[j] = ts[i];
                            xminarr[j] = i;
                            currmon = _tsToDate(ts[i]).substring(3, 5);
                        }

                    }
                    //nhathn xoa hien thi thang cua chart
                    //for (var i = 0; i < montharr.length; i++) {
                    //    var label = _tsToDate(montharr[i]).substring(3, 10);
                    //    var xlo = (c + (xminarr[i] - xmin) * cp.cwidth) - csize + this.loffset;
                    //    var xline = xlo + Math.round(csize / 2);
                    //    if (xlo > this.loffset + 20) {
                    //        _drawDashedLine(ctx, xline, h + 20, xline, this.topmargin, cs.stroke, 0.14); // ve duong ke doc // xline+3 added 17-06-2014 // h+12 chen date len tren
                    //        ctx.fillStyle = cs.label;
                    //        ctx.textAlign = "center";
                    //        ctx.fillText(label, xlo, vt + 8) // h + this.vspacing + 10); to vt+8 (display date below price chart)
                    //    }
                    //}
                }
            }
            else if (strTimeVal == -1) { // weekly chart
                if (xmax - xmin > 300) {  // year
                    var j = -1, yearDarr = [], xminarr = [];
                    for (var i = xmin; i < xmax; i++) {
                        var dateStr = this._idxToDate(i); // ts[i]; //use code for IE
                        if (dateStr.length > 10) {
                            a = dateStr.split(" ");
                            var d1 = a[0].split("-");
                            var t = a[1].split(":");
                            var d = new Date(d1[0], (d1[1] - 1), d1[2], t[0], t[1]);
                        }
                        else {
                            var d = new Date(ts[i]);
                        }

                        //var d = new Date(ts[i]);
                        var wom = d.getDate();

                        var year = d.getFullYear();
                        var curryear;

                        if (wom < 32 && year != curryear) {
                            j++;
                            yearDarr[j] = ts[i];
                            xminarr[j] = i;
                            curryear = _tsToDate(ts[i]).substring(6, 10);
                        }
                    }
                    for (var i = 0; i < yearDarr.length; i++) {
                        var label = _tsToDate(yearDarr[i]).substring(6, 10);
                        var xlo = (c + (xminarr[i] - xmin) * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);
                        if (xlo > this.loffset + 20) {
                            _drawDashedLine(ctx, xline, h + 12, xline, this.topmargin, cs.stroke, 0.14); // ve duong ke doc // xline+3 added 17-06-2014 // h+12 chen date len tren
                            ctx.fillStyle = cs.label;
                            ctx.textAlign = "center";
                            ctx.fillText(label, xlo, vt + 8) // h + this.vspacing + 10); to vt+8 (display date below price chart)
                        }
                    }
                }
                else {
                    var j = -1, quarterarr = [], xminarr = [];
                    for (var i = xmin; i < xmax; i++) {
                        var dateStr = ts[i]; //use code for IE
                        if (dateStr.length > 10) {
                            a = dateStr.split(" ");
                            var d1 = a[0].split("-");
                            var t = a[1].split(":");
                            var d = new Date(d1[0], (d1[1] - 1), d1[2], t[0], t[1]);
                        }
                        else {
                            var d = new Date(ts[i]);
                        }

                        //var d = new Date(ts[i]);
                        var wom = d.getDate();
                        var mon = d.getMonth() + 1;
                        var quarter = Math.floor((mon - 1 + 3) / 3);
                        var currquarter;

                        if (wom < 32 && quarter != currquarter) {
                            j++;
                            quarterarr[j] = ts[i];
                            xminarr[j] = i;
                            currquarter = Math.floor((parseInt(_tsToDate(ts[i]).substring(3, 5) - 1) + 3) / 3);
                        }
                    }
                    for (var i = 0; i < quarterarr.length; i++) {
                        var label = _tsToDate(quarterarr[i]).substring(3, 10);
                        var xlo = (c + (xminarr[i] - xmin) * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);
                        if (xlo > this.loffset + 20) {
                            _drawDashedLine(ctx, xline, h + 12, xline, this.topmargin, cs.stroke, 0.14); // ve duong ke doc // xline+3 added 17-06-2014 // h+12 chen date len tren
                            ctx.fillStyle = cs.label;
                            ctx.textAlign = "center";
                            ctx.fillText(label, xlo, vt + 8) // h + this.vspacing + 10); to vt+8 (display date below price chart)
                        }
                    }
                }
            }
            else if (strTimeVal == -2) { // yearly chart
                var j = -1, yeararr = [], xminarr = [];
                for (var i = xmin; i < xmax; i++) {
                    var dateStr = ts[i]; //use code for IE
                    if (dateStr.length > 10) {
                        a = dateStr.split(" ");
                        var d1 = a[0].split("-");
                        var t = a[1].split(":");
                        var d = new Date(d1[0], (d1[1] - 1), d1[2], t[0], t[1]);
                    }
                    else {
                        var d = new Date(ts[i]);
                    }

                    //var d = new Date(ts[i]);
                    var wom = d.getDate();
                    var year = d.getFullYear();
                    var curryear;

                    if (wom < 32 && year != curryear) {
                        j++;
                        yeararr[j] = ts[i];
                        xminarr[j] = i;
                        curryear = _tsToDate(ts[i]).substring(6, 10);
                    }
                }
                for (var i = 0; i < yeararr.length; i++) {
                    var label = _tsToDate(yeararr[i]).substring(6, 10);
                    var xlo = (c + (xminarr[i] - xmin) * cp.cwidth) - csize + this.loffset;
                    var xline = xlo + Math.round(csize / 2);
                    if (xlo > this.loffset + 20) {
                        _drawDashedLine(ctx, xline, h + 12, xline, this.topmargin, cs.stroke, 0.14); // ve duong ke doc // xline+3 added 17-06-2014 // h+12 chen date len tren
                        ctx.fillStyle = cs.label;
                        ctx.textAlign = "center";
                        ctx.fillText(label, xlo, vt + 8) // h + this.vspacing + 10); to vt+8 (display date below price chart)
                    }
                }
            }
            else // realtime
            {

            }



            // add indicator
            k = 1;
            for (var j in indicators) {
                this._plotIndicator(indicators[j], k);
                k += 1;
            }


            // Labels need to be added after the lines are drawn */
            var e = document.getElementById("selectts");
            var strTimeSelect = e.options[e.selectedIndex].text;
            var e1 = document.getElementById("selectscale");
            var strScale = e1.options[e1.selectedIndex].text;
            /* And now let's give it a label. */ // draw text VOLUME, TickerNm, CopyRight nqhuy
            if (this.cp.label) {
                this._drawText("Volume = " + numberWithCommas(Math.round(volCurrent * 10) / 10), vl, vt + 25, { align: 'left', padding: 5, font: '8pt Arial', color: color });
                this._drawText(this.cp.label.toUpperCase(), this.loffset - 2, 24, { align: 'left', font: '10pt Arial' });
                this._drawText(this.copyrighttext, this.width - 80, 24, { align: 'right', padding: 5, font: '10pt Arial' });
                if (strScale == "Log") {
                    this._drawText(strTimeSelect + " " + _tsToDate(pdate) + ", Open " + Math.round(_exp((popen / scale + cp.ymin)) * 100) / 100 + ", High " + Math.round(_exp((phigh / scale + cp.ymin)) * 100) / 100 + ", Low " + Math.round(_exp((plow / scale + cp.ymin)) * 100) / 100 + ", Close " + Math.round(_exp((pclose / scale + cp.ymin)) * 100) / 100 + ", Volume " + numberWithCommas(Math.round(volCurrent * 10) / 10), this.loffset + 100, 24, { align: 'left', font: '8pt Arial' });
                }
                else {
                    this._drawText(strTimeSelect + " " + _tsToDate(pdate) + ", Open " + Math.round((popen / scale + cp.ymin) * 100) / 100 + ", High " + Math.round((phigh / scale + cp.ymin) * 100) / 100 + ", Low " + Math.round((plow / scale + cp.ymin) * 100) / 100 + ", Close " + Math.round((pclose / scale + cp.ymin) * 100) / 100 + ", Volume " + numberWithCommas(Math.round(volCurrent * 10) / 10), this.loffset + 100, 24, { align: 'left', font: '8pt Arial' });
                }
                var ol = this.cs.overlays;
                //                for (var i in overlays) {
                //                    if (overlays[i].offset !== undefined) {
                //                        var o = overlays[i].offset;
                //                        this._drawLegend(i, o);
                //                    }
                //                }
            }

        },


        _clear: function (ctx) {
            ctx.clearRect(0, 0, this.width, this.height);
            ctx.fillStyle = this.cs.background;
            ctx.fillRect(0, 0, this.width, this.height);
        },
        /* 
        * We assume the data is already downloaded using probably ajax or a local copy.  We just read this data in the TA object. If there are any known indicators in the template, we fire and store those in the TA object supported formats: json array, or JS array. Anything else, we don't process as of now Upon completion, done function is called - first parameter is 'TA object' second parameter is error code.  TODO : implement done function functionality completely.*/
        read: function (title, data, done) {
            // We expect the data to be in the following form 
            // { label : 'label for the chart', data: [[ts,o,h,l,c,v], [ts,o,h,l,c,v],....] }
            infodiv = 110;
            //New chart param. We are not going to plot before read anyways.
            this.cp = new $.tickp.chartparams.init(this);
            var errorcode = 0;
            var label = '';
            if (!data.label || !data.data) {
                // see if it's an array.
                if (isArray(data)) {
                    if (!(data.length)) { // empty array 
                        return false;
                    }
                    var d = data;
                }
            }
            if (data.data) {
                if (isArray(data.data)) {
                    if (!data.data.length) { // empty array
                        return false;
                    }
                    var d = data.data;
                }
                if (data.label) {
                    label = data.label;
                }
                else {
                    label = title;
                }
            }
            if (data.label) {
                label = data.label;
            }
            else {
                label = title;
            }
            // neither data or data.data was an array, try to parse json or else give up 
            if (!d) {
                try {
                    errorcode = 0;
                    var d = parseJSON(data);
                    if (d.data) {
                        if (isArray(d.data)) {
                            label = d.label;
                            d = d.data;
                        } else {
                            errorcode = -3;
                        }
                    } else if (!isArray(d)) {
                        errorcode = -3;
                    }
                } catch (e) {
                    alert(e);  // FIXME: Is not portable. I guess alert is okay for portability
                    errorcode = -2; // JSON failure.
                }
            }

            if (errorcode) {
                if (isFunction(done)) { // Data not good 
                    return false; // FIXME : revisit this later
                    return done.call(this, errorcode);
                } else {
                    return false;
                }
            }
            /*  We Got Data now. We separate this into timestamps and OHLC
            The advantage of separating these two is that, later when we 
            need slices of the data, we'd use the timestamps to obtain 
            indices and then use those offsets in ohlc and indicators.
            it'd be efficient goin through the timestamps table */
            this.current = { ts: [], ohlc: [], vol: [], overlays: {}, indicators: [] };
            this.cp.numindicators = 0; // Canvas uses this value. Need to be reset
            this.cp.label = label; // FIXME: The data should take care of it 
            var ts = []; //this.current.ts; //just for convenience
            var ohlc = []; //this.current.ohlc; //just for convenience
            var v = []; // this.current.vol;
            if (!d) {
                var d = data;
            }
            for (var i in d) {
                if (d[i].length != 0) {
                    ts[i] = _getDateTs(d[i][0]);

                    ohlc[i] = [d[i][1], d[i][2], d[i][3], d[i][4]];
                    //if (d[i][5]) {
                    v[i] = d[i][5];
                }
                //}
            }

            this.dailydata = undefined;
            this.monthlydata = undefined;
            this.weeklydata = undefined;
            this.dailydata = { ts: ts, ohlc: ohlc, vol: v };
            if (this.interval == -1) {
                this.timescale(ts, ohlc, v, 'weekly');
            }
            if (this.interval == -2) {
                this.timescale(ts, ohlc, v, 'monthly');
            }
            this.setTimeScale(this.interval);
            return true;
        },

        // Added nqhuy Heikin-Ashi 13-05-2014
        readHeikin: function (title, data, done) {
            // We expect the data to be in the following form 
            // { label : 'label for the chart', data: [[ts,o,h,l,c,v], [ts,o,h,l,c,v],....] } 

            //New chart param. We are not going to plot before read anyways.
            this.cp = new $.tickp.chartparams.init(this);
            var errorcode = 0;
            var label = '';
            if (!data.label || !data.data) {
                // see if it's an array.
                if (isArray(data)) {
                    if (!(data.length)) { // empty array 
                        return false;
                    }
                    var d = data;
                }
            }
            if (data.data) {
                if (isArray(data.data)) {
                    if (!data.data.length) { // empty array
                        return false;
                    }
                    var d = data.data;
                }
                if (data.label) {
                    label = data.label;
                }
                else {
                    label = title;
                }
            }
            if (data.label) {
                label = data.label;
            }
            else {
                label = title;
            }
            // neither data or data.data was an array, try to parse json or else give up 
            if (!d) {
                try {
                    errorcode = 0;
                    var d = parseJSON(data);
                    if (d.data) {
                        if (isArray(d.data)) {
                            label = d.label;
                            d = d.data;
                        } else {
                            errorcode = -3;
                        }
                    } else if (!isArray(d)) {
                        errorcode = -3;
                    }
                } catch (e) {
                    alert(e);  // FIXME: Is not portable. I guess alert is okay for portability
                    errorcode = -2; // JSON failure.
                }
            }

            if (errorcode) {
                if (isFunction(done)) { // Data not good 
                    return false; // FIXME : revisit this later
                    return done.call(this, errorcode);
                } else {
                    return false;
                }
            }
            /*  We Got Data now. We separate this into timestamps and OHLC
            The advantage of separating these two is that, later when we 
            need slices of the data, we'd use the timestamps to obtain 
            indices and then use those offsets in ohlc and indicators.
            it'd be efficient goin through the timestamps table */
            this.current = { ts: [], ohlc: [], vol: [], overlays: {}, indicators: [] };
            this.cp.numindicators = 0; // Canvas uses this value. Need to be reset
            this.cp.label = label; // FIXME: The data should take care of it 
            var ts = []; //this.current.ts; //just for convenience
            var ohlc = []; //this.current.ohlc; //just for convenience
            var v = []; // this.current.vol;
            if (!d) {
                var d = data;
            }
            var ohlcTemp = [];

            for (var i in d) {
                ts[i] = _getDateTs(d[i][0]);
                ohlcTemp[i] = [d[i][1], d[i][2], d[i][3], d[i][4]];
                if (i == 0) {
                    var yclHA = (d[0][1] + d[0][2] + d[0][3] + d[0][4]) / 4;
                    var yopHA = (yclHA + d[0][1]) / 2;
                    var yhiHA = Math.max(yopHA, d[0][2], yclHA);
                    var yloHA = Math.min(yopHA, d[0][3], yclHA);
                    ohlc[0] = [yopHA, yhiHA, yloHA, yclHA];
                }
                else {
                    yclHA = (d[i][1] + d[i][2] + d[i][3] + d[i][4]) / 4;
                    yopHA = (d[i - 1][1] + d[i - 1][4]) / 2;
                    var yhiHA = Math.max(yopHA, d[i][2], yclHA);
                    var yloHA = Math.min(yopHA, d[i][3], yclHA);
                    ohlc[i] = [yopHA, yhiHA, yloHA, yclHA];
                }
                if (d[i][5]) {
                    v[i] = d[i][5];
                }
            }


            this.dailydata = undefined;
            this.monthlydata = undefined;
            this.weeklydata = undefined;
            this.dailydata = { ts: ts, ohlc: ohlc, vol: v };
            if (this.interval == -1) {
                this.timescale(ts, ohlc, v, 'weekly');
            }
            if (this.interval == -2) {
                this.timescale(ts, ohlc, v, 'monthly');
            }
            this.setTimeScale(this.interval);

            return true;
        },

        timescale: function (ts, data, volume, tmscale) {
            var cwi = -1, lwi = -1;
            var wohlc = [], wts = [], v = [];
            var whi, wlo;

            var pwd = (tmscale == 'weekly' ? 7 : 32);
            for (var i = 0; i < data.length; i++) {
                var dt = data[i];
                var d = new Date(ts[i]);
                var wom = (tmscale == 'weekly' ? d.getDay() : d.getDate());
                if (wom < pwd) { // new week has started 
                    cwi++;
                    wohlc[cwi] = [dt[0], dt[1], dt[2], dt[3]];
                    wts[cwi] = ts[i]; //display begin date of each periods 
                    v[cwi] = volume[i];
                    whi = dt[1];
                    wlo = dt[2];
                } else {
                    if (dt[1] > whi) {
                        whi = dt[1];
                        wohlc[cwi][1] = whi;
                    }
                    if (dt[2] < wlo) {
                        wlo = dt[2];
                        wohlc[cwi][2] = wlo;
                    }
                    wohlc[cwi][3] = dt[3];
                    v[cwi] += volume[i];
                    wts[cwi] = ts[i]; // display end date of each periods
                }
                pwd = wom;
            }
            if (tmscale == 'weekly') {
                this.weeklydata = { ts: wts, ohlc: wohlc, vol: v };
            }
            if (tmscale == 'monthly') {
                this.monthlydata = { ts: wts, ohlc: wohlc, vol: v };
            }
        },

        setTimeScale: function (ts) {
            var ts = parseInt(ts);
            if (isNaN(ts)) ts = 0;
            this.interval = ts;
            if (!this.dailydata) return; // just in case. 
            switch (ts) {
                case 0:
                    this.current.ohlc = this.dailydata.ohlc;
                    this.current.ts = this.dailydata.ts;
                    this.current.vol = this.dailydata.vol;
                    this.cp.end = this.dailydata.ohlc.length;
                    break;
                case -1:

                    if (!this.weeklydata) {
                        this.timescale(this.dailydata.ts, this.dailydata.ohlc, this.dailydata.vol, 'weekly');
                    }
                    this.current.ohlc = this.weeklydata.ohlc;
                    this.current.ts = this.weeklydata.ts;
                    this.current.vol = this.weeklydata.vol;
                    this.cp.end = this.weeklydata.ohlc.length;
                    break;
                case -2:
                    if (!this.monthlydata) {
                        this.timescale(this.dailydata.ts, this.dailydata.ohlc, this.dailydata.vol, 'monthly');
                    }
                    this.current.ohlc = this.monthlydata.ohlc;
                    this.current.ts = this.monthlydata.ts;
                    this.current.vol = this.monthlydata.vol;
                    this.cp.end = this.monthlydata.ohlc.length;
                    break;
                default:
                    this.current.ohlc = this.daily.ohlc;
                    this.current.ts = this.daily.ts;
                    this.current.vol = this.daily.vol;
                    break;
            }
            // We now delete all indicators and overlays.  
            // Remember : Always use delindicator to delete an indicator. It keeps the internal
            // plot structure consistent. 
            var all = this.getindicators();
            for (var i in all) {
                this.delindicator(all[i]);

            }
            this.plot();

        },

        /* zooming.. basically this is very simple . We change the cpminwidth 
        value and replot
        */
        zoom: function (up, data) {
            if (up) {
                if (this.cp.minwidth > 50) {
                    this.cp.minwidth = 50; // modified 
                }
                else if (this.cp.minwidth > 5) {
                    this.cp.minwidth += 1;
                }
                else if (this.cp.minwidth > 3) {
                    this.cp.minwidth += 0.5;
                }
                else
                    this.cp.minwidth += 0.1;

            } else {
                //this.cp.minwidth -= 0.1;
                if (this.cp.minwidth < 0.1) {// modified 
                    this.cp.minwidth = 0.1;  // modified 
                }
                else if (this.cp.minwidth > 40) {
                    this.cp.minwidth -= 10;
                }
                else if (this.cp.minwidth > 5) {
                    this.cp.minwidth -= 1;
                }
                else if (this.cp.minwidth > 3) {
                    this.cp.minwidth -= 0.5;
                }
                else
                    this.cp.minwidth -= 0.1;
            }
            var e = document.getElementById("zoomwidth");
            e.value = this.cp.minwidth;

            var cp = this.cp;
            var begin, end, howmany = 0;
            var w = this.plotwidth();
            howmany = data.length;
            if (howmany > (w / cp.minwidth)) {
                howmany = Math.round(w / cp.minwidth);
            }

            cp.end = cp.end || data.length; // first time we set it to end
            if (cp.end > data.length) {
                cp.end = data.length;
            }
            this.cp.xmin = cp.end - howmany;
            if (cp.xmin < 0) {
                cp.xmin = 0;
            }
            this.plot();
        },

        plotminwidth: function (minwidth, data) {
            this.cp.minwidth = minwidth;
            var cp = this.cp;
            var begin, end, howmany = 0;
            var w = this.plotwidth();
            howmany = data.length;
            if (howmany > (w / cp.minwidth)) {
                howmany = Math.round(w / cp.minwidth);
            }

            cp.end = cp.end || data.length; // first time we set it to end
            if (cp.end > data.length) {
                cp.end = data.length;
            }
            this.cp.xmin = cp.end - howmany;
            if (cp.xmin < 0) {
                cp.xmin = 0;
            }
            this.plot();
        },

        zoomtype: function (zt, data) {
            //zt = 3;//nhathn set zoom mac dinh la 1 nam
            var w = this.plotwidth();

            var startdate;
            var xmin;
            // error on IE
            //var date = new Date(data[data.length - 1][0]); // lastdate of data
            var dateStr = data[data.length - 1][0];
            if (dateStr != undefined && dateStr.length > 10) {
                var a = dateStr.split(" ");
                var d = a[0].split("-");
                var t = a[1].split(":");
                var date = new Date(d[0], (d[1] - 1), d[2], t[0], t[1]);
            }
            else {
                var date = new Date(dateStr);
            }
            if (zt == 0) {
                this.cp.minwidth = w * 12 / 250;
                startdate = date.setFullYear(date.getFullYear(), date.getMonth() - 1);
            }
            else if (zt == 1) {
                this.cp.minwidth = w * 12 / (250 * 3);
                startdate = date.setFullYear(date.getFullYear(), date.getMonth() - 3);
            }
            else if (zt == 2) {
                this.cp.minwidth = w * 12 / (250 * 6);
                startdate = date.setFullYear(date.getFullYear(), date.getMonth() - 6);
            }
            else if (zt == 3) {
                this.cp.minwidth = w * 12 / (250 * 12);
                startdate = date.setFullYear(date.getFullYear(), date.getMonth() - 12);
            }
            else if (zt == 4) {
                this.cp.minwidth = w * 12 / (250 * 48);
                startdate = date.setFullYear(date.getFullYear(), date.getMonth() - 48);
            }
            else if (zt == 5) { //all
                this.cp.minwidth = 0.1;
            }
            else if (zt == 6) { // YTD
                this.cp.minwidth = 0.1;
                startdate = (new Date(new Date().getFullYear(), 0, 1)).setFullYear(date.getFullYear());
            }
            var e = document.getElementById("zoomwidth");
            e.value = this.cp.minwidth;
            for (var i = 0; i < data.length; i++) {

                var debug = _tsToDate(startdate);
                var dt = data[i][0];
                var d2 = dt.split("-");
                var ts = new Date(d2[0], (d2[1] - 1), d2[2]); //new Date(dt[0]);
                var debug2 = _tsToDate(ts.setFullYear(ts.getFullYear()));
                if (startdate <= ts.setFullYear(ts.getFullYear())) {
                    this.cp.xmin = i;
                    break;
                }
            }
            if (zt == 5) {
                this.cp.xmin = 0;
            }
            this.cp.zt = zt;
            this.plot();
            //return { xmin: xmin };
        },

        _volgrid: function (maxvol, howmany) {
            var labels = [];
            var lookup = [1000000000.0, 1000000.0, 1000.0, 1.0];
            var avols = [];
            for (var i = 0; i < howmany; i++) {
                avols.push(maxvol / (howmany - i));
            }
            var suffix = ['B', 'M', 'K', ''];
            for (var i in lookup) {
                if (avols[0] / lookup[i] > 1) {
                    for (var j in avols) {
                        var n = Math.round(((avols[j]) / lookup[i]) * 10) / 10 + suffix[i]; //(avols[j] - avols[j] % lookup[i]) / lookup[i] + suffix[i];
                        labels.push(n);
                    }
                }
            }
            return labels[0];
        },

        _obvgrid: function (maxvol, howmany) {
            var labels = [];
            var lookup = [1000000000.0, 1000000.0, 1000.0, 1.0];
            var avols = [];
            for (var i = 0; i < howmany; i++) {
                avols.push(maxvol / (howmany - i));
            }
            var suffix = ['B', 'M', 'K', ''];
            for (var i in lookup) {
                if (Math.abs(avols[0]) / lookup[i] > 1) {
                    for (var j in avols) {
                        var n = avols[j] > 0 ? Math.round(((avols[j]) / lookup[i]) * 10) / 10 + suffix[i] : '-' + Math.round((Math.abs(avols[j]) / lookup[i]) * 10) / 10 + suffix[i];
                        labels.push(n);
                    }
                }

            }
            if (avols[0] <= 0) {
                labels.push(avols[0]);
            }
            return labels[0];
        },

        /* The function below returns an array of values for Y axis for Grid
        Display. Basic algorithm is given below - 
        Figure out the closest separator value from the lookup 
        for given input and then return ceil of min to floor of max times the
        separator in an array. Those will be our grid points. 
        Loved, this piece of code. Not brilliant, but very clever, hopefully
        should scale from Penny stocks to Zimbabwe market */
        _ygrid: function (ymin, ymax, howmany) {
            var approx = (ymax - ymin) / howmany;
            lookup = [0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 25.0, 50.0,
                 100.0, 250.0, 500.0, 1000.0, 2500.0, 5000.0,
                 10000.0, 25000.0, 50000.0, 100000.0, 500000.0, 1000000.0];

            var na = []
            for (i in lookup) {
                var b = lookup[i] / approx;
                if (b < 1.0) { b = 1 / b; }
                na.push(b);
            }
            var closest = lookup[na.indexOf(Math.min.apply(this, na))];
            var minindex = Math.ceil(ymin / closest);
            var maxindex = Math.floor(ymax / closest);
            vals = [];
            for (var j = minindex; j <= maxindex; j++) {
                vals.push(j * closest);
            }

            return vals;
        },

        _vygrid: function (vymin, vymax, howmany) {
            var approx = (vymax) / howmany;
            lookup = [10000.0, 20000.0, 40000.0, 50000.0, 60000.0, 80000.0,
                    100000.0, 200000.0, 400000.0, 500000.0, 600000.0, 800000.0,
                    1000000.0, 2000000.0, 4000000.0, 5000000.0, 6000000.0, 8000000.0,
                    10000000.0, 20000000.0, 40000000.0, 50000000.0, 60000000.0, 80000000.0,
                    100000000.0, 200000000.0, 400000000.0, 500000000.0, 600000000.0, 800000000.0,
                    1000000000.0, 2000000000.0, 4000000000.0, 5000000000.0, 6000000000.0, 8000000000.0, ];

            //            var na = []
            //            for (i in lookup) {
            //                var b = lookup[i] / approx;
            //                if (b < 1.0) { b = 1 / b; }
            //                na.push(b);
            //            }
            var closest = this._closest(approx, lookup); // lookup[na.indexOf(Math.min.apply(this, na))];
            var minindex = Math.ceil(vymin / closest);
            var maxindex = Math.floor(vymax / closest);
            vals = [];
            for (var j = minindex; j <= maxindex; j++) {
                vals.push(j * closest);
            }

            return vals;
        },

        _normalgrid: function (ymin, ymax, howmany) {
            var approx = (ymax - ymin) / howmany;
            lookup = [

                 0.5,
                 1.0, 1.5,
                 2.0, 2.5,
                 3.0, 3.5,
                 4.0, 4.5,
                 5.0, 5.5,
                 6.0, 6.5,
                 7.0, 7.5,
                 8.0, 8.5, 9.0, 9.5, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0,
                 20.0, 22.0, 24.0, 26.0, 28.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0,
                 100.0];

            var na = []
            for (i in lookup) {
                var b = lookup[i] / approx;
                if (b < 1.0) { b = 1 / b; }
                na.push(b);
            }
            var closest = lookup[na.indexOf(Math.min.apply(this, na))];
            var minindex = Math.ceil(ymin / closest);
            var maxindex = Math.floor(ymax / closest);
            vals = [];
            for (var j = minindex; j <= maxindex; j++) {
                vals.push(j * closest);
            }

            return vals;
        },



        _closest: function (num, arr) {
            var curr = arr[0];
            var diff = Math.abs(num - curr);
            for (var val = 0; val < arr.length; val++) {
                var newdiff = Math.abs(num - arr[val]);
                if (newdiff < diff) {
                    diff = newdiff;
                    curr = arr[val];
                }
            }
            return curr;
        },

        _indigrid: function (ymin, ymax, howmany) // for obv
        {
            var approx = (ymax - ymin) / howmany;
            lookup = [
                    -10000.0, -20000.0, -40000.0, -50000.0, -60000.0, -80000.0,
                    -100000.0, -200000.0, -400000.0, -500000.0, -600000.0, -800000.0,
                    -1000000.0, -2000000.0, -4000000.0, -5000000.0, -6000000.0, -8000000.0,
                    -10000000.0, -20000000.0, -40000000.0, -50000000.0, -60000000.0, -80000000.0,
                    -100000000.0, -200000000.0, -400000000.0, -500000000.0, -600000000.0, -800000000.0,
                    1000.0,
                    10000.0, 20000.0, 40000.0, 50000.0, 60000.0, 80000.0,
                    100000.0, 200000.0, 400000.0, 500000.0, 600000.0, 800000.0,
                    1000000.0, 2000000.0, 4000000.0, 5000000.0, 6000000.0, 8000000.0,
                    10000000.0, 20000000.0, 40000000.0, 50000000.0, 60000000.0, 80000000.0,
                    100000000.0, 200000000.0, 400000000.0, 500000000.0, 600000000.0, 800000000.0,
                    1000000000.0, 2000000000.0, 4000000000.0, 5000000000.0, 6000000000.0, 8000000000.0,
                    10000000000.0];

            var closest = this._closest(approx, lookup);
            var minindex = Math.ceil(ymin / closest);
            var maxindex = Math.floor(ymax / closest);
            vals = [];
            for (var j = minindex; j <= maxindex; j++) {
                vals.push(j * closest);
            }
            //vals.push(ymax);
            //vals.push(ymin);
            return vals;
        },

        _indiMomen: function (ymin, ymax, howmany) // for Momen
        {
            howmany = 3;
            var approx = (ymax - ymin) / howmany;
            lookup = [10, 20, 30, 40, 50, 60,
                 70.0,
                 80.0, 90.0,
                 100.0, 110.0, 120.0];

            var na = []
            for (i in lookup) {
                var b = lookup[i] / approx;
                if (b < 1.0) { b = 1 / b; }
                na.push(b);
            }
            var closest = lookup[na.indexOf(Math.min.apply(this, na))];
            var minindex = Math.ceil(ymin / closest);
            var maxindex = Math.floor(ymax / closest);
            vals = [];
            //for (var j = minindex; j <= maxindex; j++) {
            //    vals.push(j * closest);
            //}
            //nhathn chi hien thi 3 cot
            vals.push(closest * minindex);
            if ((closest * maxindex) > 0 > (closest * minindex)) {
                vals.push(0);
            }
            vals.push(closest * maxindex);
            //end

            return vals;
        },

        _indiHundred: function (ymin, ymax, howmany) // for adx
        {
            var approx = (ymax - ymin) / howmany;
            lookup = [

                 20.0, 40.0, 60.0, 80.0,
                 100.0];

            var na = []
            for (i in lookup) {
                var b = lookup[i] / approx;
                if (b < 1.0) { b = 1 / b; }
                na.push(b);
            }
            var closest = lookup[na.indexOf(Math.min.apply(this, na))];
            var minindex = Math.round(ymin / closest);
            var maxindex = Math.round(ymax / closest);
            vals = [];
            for (var j = minindex; j <= maxindex; j++) {
                vals.push(j * closest);
            }

            return vals;
        },

        _indiMACD: function (ymin, ymax, howmany) // for macd
        {
            var approx = ymin;
            lookup = [
                 -50,
                 -48, -46, -45, -44, -42, -40,
                 -38, -36, -35, -34, -32, -30,
                 -28, -26, -25, -24, -22, -20,
                 -18, -16, -15, -14, -12, -10,
                 -8, -6, -5, -4, -2, -1,
                  0.0, 1.0, 2.0, 4.0, 5.0, 6.0, 8.0, 10.0,
                  12, 14, 15.0, 16, 18, 20.0,
                  22, 24, 25, 26, 28, 30.0,
                  32, 34, 35, 36, 38, 40,
                  42, 44, 45, 46, 48, 50.0];

            //var na = []
            //for (i in lookup) {
            //    var b = lookup[i] / approx;
            //    if (b < 1.0) { b = 1 / b; }
            //    na.push(b);
            //}
            var closest = this._closest(approx, lookup); //lookup[na.indexOf(Math.min.apply(this, na))];
            var minindex = Math.round(ymin / closest);
            var maxindex = Math.round(ymax / closest);
            if (minindex > maxindex) {
                var temp = maxindex;
                maxindex = minindex;
                minindex = temp;
            }
            //minindex = minindex > maxindex ? maxindex : minindex;
            //maxindex = minindex > maxindex ? minindex : maxindex;
            vals = [];
            //for (var j = -1; j <= 1; j++) {
            //    vals.push(j * closest);
            //}
            vals.push(closest);
            vals.push(this._closest(ymax, lookup));

            return vals;
        },

        /* when we plot we are not going to plot more than _window amount of 
        data. So we call this function before plotting. Or it might get
        automatically called depending upon date range being too long
        TODO: Implement date ranges. 
        This is the most important internal function in plotting. 
        This function, first determines the range of data that is to be
        plot. Then it figures out the chart specific parameters. Most 
        important being (xmin, xmax, ymin, ymax) and it returns these
        values to the caller. But this function just doesn't do that much
        it also - determines whether 'log' mode is required. 
        it also - initiates the values of the chartparams object (which
        the drawing function would later use.) 
        */
        _window: function (data, overlays, shift, datelo, datehi) {

            /* right now only intializes chart params using data.*/
            var cp = this.cp;

            var w = this.plotwidth();
            /* Determine howmany candles to display and get their begin and
            end offsets. */
            var begin, end, howmany = 0;

            shift = shift || 0;
            cp.candles = cp.candles || data.length;
            howmany = data.length;
            if (howmany > (w / cp.minwidth)) {
                howmany = Math.round(w / cp.minwidth);
            }

            cp.end = cp.end || data.length; // first time we set it to end
            cp.end -= shift;
            if (cp.end > data.length) {
                cp.end = data.length;
            }
            cp.begin = cp.end - howmany;
            if (cp.begin < 0) {
                cp.begin = 0;
                cp.end = cp.begin + howmany;
            }
            begin = cp.begin;

            if (shift == 0) {
                begin = cp.xmin;
                howmany = cp.end - begin > 0 ? cp.end - begin : cp.end - cp.begin;
                begin = cp.end - begin > 0 ? cp.xmin : cp.begin;

            }
            else {
                howmany = cp.end - begin;
            }
            end = cp.end;
            cp.minwidth = w / howmany;

            // -- nasty code begins : Note above code substantially simpliefies it,
            //    still, I'd keep this for some time and get rid of it, once I am convinced
            /* if (!(shift === undefined)) {
            if(cp.candles == data.length) { // no panning is required

                    return { xmin:cp.begin, xmax: cp.end, ymin:cp.ymin, ymax:cp.ymax};
            } else { 
            howmany = cp.candles;
            } 
            begin = cp.begin - shift;
            if (begin < 0) {
            begin = 0; 
            } 
            } else {
            howmany = data.length;

                if(howmany > ((w)/cp.minwidth)) {
            howmany = Math.round(w/cp.minwidth);
            } 
            begin = data.length - howmany;
            } 
            end = begin + howmany;
            if (end > data.length) { 
            end = data.length;
            begin = end - howmany;
            }  */

            // -- nasty code ends 

            /* Stuff needed to determine width of candles */
            cp.candles = howmany;
            cp.cwidth = ((w) / cp.candles);

            /* Y range is going to be dynamic get, min and max from data */
            var max, min;
            var d_ = _minmax2d(data.slice(begin, end));
            min = d_[0], max = d_[1];

            /* Indicators overlayed should fit in the frame. So determine the 
            real 'min/max' by using the overlays data as well.  */
            if (overlays) {
                for (var j in overlays) {
                    var omax, omin;
                    if (!j.search('bbands')) {
                        d_ = _minmax2d(overlays[j].data.slice(begin, begin + howmany));
                    } else {
                        d_ = _minmax1d(overlays[j].data.slice(begin, begin + howmany));
                    }
                    omin = d_[0], omax = d_[1];
                    if (omax > max) max = omax;
                    if (omin < min) min = omin;
                }
            }

            cp.begin = begin;
            cp.end = end;
            if ((max / min > 2.0) && cp.autoscale) {
                cp.logscale = true;
            }
            var range = max - min;
            cp.ymin = min - (0.05 * range); // little margin below
            cp.ymax = max + (0.05 * range); // little margin above 

            if (cp.logscale) {
                min = 0.9 * min;
                max = 1.1 * max;
                cp.ymin = Math.log(min);
                if (isNaN(cp.ymin)) {
                    /* bollingers might go negative n stock split, for which Math.log is not defined. 
                    we set the min to 0 for now */
                    cp.ymin = 0;
                }
                cp.ymax = Math.log(max);
            }
            return { xmin: begin, xmax: end, ymin: cp.ymin, ymax: cp.ymax };
        },

        _idxToDate: function (i) {
            var ts = this.current.ts[i];
            return _tsToDate(ts);
        },

        _plotIndicator: function (i, o) {
            var data = i.data;
            var type = i.type;
            var str = i.str;
            var t = this.topmargin + this.plotht + (o * this.loweriht) + this.vspacing - 58; //nhathn tinh khoang cach de dua Indicator len gan voi candlestick
            var b = t + this.liplotht + this.limargin;
            var l = this.loffset;
            var r = this.loffset + this.plotwidth();
            var begin = this.cp.begin;
            var end = this.cp.end;

            var cp = this.cp;
            var cs = this.cs;

            ctx = this.ctx;
            ctx.strokeStyle = this.cs.stroke;
            ctx.lineWidth = 0.5;
            if (screen.width >= 1300)
                ctx.strokeRect(l - 5 - .5, t - .5, this.plotwidth() + 5, this.liplotht + this.limargin);
            else if (screen.width >= 1100)
                ctx.strokeRect(l - 5 + .2, t + .2, this.plotwidth() + 5, this.liplotht + this.limargin + .2);
            else
                ctx.strokeRect(l - 5 + .3, t + .3, this.plotwidth() + 5, this.liplotht + this.limargin);


            var c = Math.round(cp.cwidth / 2); // center of first (and every) cdl 
            var csize = Math.round(cp.cwidth / 1.6);
            switch (type) {
                case 'macd':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax2d(data.slice(begin, end));
                    var ymax = d[1], ymin = d[0];
                    var range = (ymax - ymin);
                    ymax = ymax + 0.1 * range;
                    ymin = ymin - 0.1 * range;
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    for (var j = begin; j < end; j++) {
                        if (data[j][0] === undefined) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);
                        var mhi = (data[j][0] - ymin) * scale;
                        var mlo = (data[j][1] - ymin) * scale;
                        var mzero = Math.round((0 - ymin) * scale);
                        if (prevxy[0]) { //skip first line
                            _drawline(ctx, prevxy[0][0], prevxy[0][1], xline, h - mhi, 'blue', 1); //macd
                            _drawline(ctx, prevxy[1][0], prevxy[1][1], xline, h - mlo, cs.adxcolor[1], 1); //signal                             
                            ctx.fillStyle = cs.stroke;
                            // FIXME : Fix for opera, check if it works
                            if (mhi - mlo > 0) {
                                ctx.fillRect(xlo, h - mzero - (mhi - mlo), csize, (mhi - mlo));
                            } else {
                                ctx.fillRect(xlo, h - mzero, csize, (mlo - mhi));
                            }
                        }
                        prevxy = [[xline, h - mhi], [xline, h - mlo]];
                    }
                    this._drawText(str + ' = ' + Math.round((mhi / scale + ymin) * 100) / 100, l, t + 12, { align: 'left', padding: 5, font: '8pt Arial', color: cs.adxcolor[0] });
                    this._drawText(', Signal' + str.replace('MACD', '') + ' = ' + Math.round((mlo / scale + ymin) * 100) / 100, l + 120, t + 12, { align: 'left', padding: 5, font: '8pt Arial', color: cs.adxcolor[1] });

                    ystops = [mhi / scale + ymin, mlo / scale + ymin];
                    //ystops = this._indiMACD((ymin), (ymax), 2);
                    //for (var i in ystops) {
                    //    var logystp = (ystops[i]);
                    //    var y1 = Math.round((logystp - ymin) * scale);
                    //    //if (y1 > 1) { // don't draw anything in first 20 pixels
                    //    _drawDashedLine(ctx, this.loffset - 5, h - y1, this.loffset + this.plotwidth() + 5, h - y1, cs.stroke, 0.14);   // ve duong ke ngang Price
                    //    _drawline(ctx, this.loffset + this.plotwidth(), h - y1, this.loffset + this.plotwidth() + 4, h - y1, cs.stroke, 0.5); // thang chia do
                    //    label = Math.round(ystops[i] * 10) / 10;

                    //    //this._drawText(label, r, h - y1, { align: 'left', padding: 5, font: '8pt Arial' }); // label duong ke ngang Price
                    //    //}
                    //};
                    var y1 = Math.round((0 - ymin) * scale);
                    if (strBackground == 1) {
                        _drawDashedLine(ctx, this.loffset - 5 - .5, h - y1 - .5, this.loffset + this.plotwidth() + 5, h - y1, cs.colorover, 0.5); //cs.macd[1]
                        _drawline(ctx, this.loffset + this.plotwidth() - .5, h - y1 - .5, this.loffset + this.plotwidth() + 4, h - y1, cs.colorover, 0.5);
                    }
                    else {
                        _drawline(ctx, this.loffset - 5 - .5, h - y1 - .5, this.loffset + this.plotwidth() + 5, h - y1, cs.rsicolor, 0.5);   // ve duong ke ngang Price
                        _drawline(ctx, this.loffset + this.plotwidth() - .5, h - y1 - .5, this.loffset + this.plotwidth() + 4, h - y1, cs.rsicolor, 0.5); // thang chia do
                    }
                    label = 0;

                    this._drawText(label, r, h - y1 + 3, { align: 'left', padding: 5, font: '7pt Arial' }); // label duong ke ngang Price
                    break;
                case 'rsi':
                    var d = _minmax1d(data.slice(begin, end));
                    //var ymax = d[1], ymin = 0;
                    var ymax = 1, ymin = 0;
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b - 5;
                    prevxy = [];
                    for (var j = begin; j < end; j++) {
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);
                        var rsi = (data[j] - ymin) * scale;
                        var currenty = h - rsi;
                        //var p80 = h - (0.8 - ymin) * scale;
                        if (prevxy[0]) { //skip first line                
                            //if (currenty < p80 && prevxy[1] < p80)
                            //    _drawline(ctx, prevxy[0], prevxy[1], xline, currenty, 'red', 1); //nhathn sua mau duong RSI                            
                            //else if (prevxy[1] > p80 && currenty < p80) {
                            //    _drawline(ctx, prevxy[0], prevxy[1], xline, p80, cs.rsiMainColor, 1); //nhathn sua mau duong RSI                            
                            //    _drawline(ctx, prevxy[0], p80, xline, currenty, 'red', 1); //nhathn sua mau duong RSI                            
                            //}
                            //else if (prevxy[1] < p80 && currenty > p80) {
                            //    _drawline(ctx, prevxy[0], prevxy[1], xline, p80, 'red', 1); //nhathn sua mau duong RSI                            
                            //    _drawline(ctx, prevxy[0], p80, xline, currenty, cs.rsiMainColor, 1); //nhathn sua mau duong RSI                 
                            //}
                            //else
                            _drawline(ctx, prevxy[0], prevxy[1], xline, currenty, 'yellow', 0.5); //nhathn sua mau duong RSI                            
                        }
                        prevxy = [xline, currenty];
                    }
                    this._drawText(str + ' = ' + Math.round(rsi / scale * 100) / 100, l, t + 10, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rsiMainColor });
                    ystops = [0.2, 0.8]; //nhathn 2 cot 30,70 cua rsi

                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        //if (j == 1)
                        //    _drawDashedLine(ctx, this.loffset - 5 - 0.5, h - ystop - 0.5, this.loffset + this.plotwidth() + 5 - 0.5, h - ystop, cs.stroke, 0.25);   // ve duong ke ngang Price
                        //else if(j==0)
                        //    _drawline(ctx, this.loffset - 5 - 0.5, h - ystop - 0.5, this.loffset + this.plotwidth() + 5 - 0.5, h - ystop - 0.5, 'green', 1); //cs.macd[1]                        
                        //else if (j == 2)
                        //    _drawline(ctx, this.loffset - 5 - 0.5, h - ystop - 0.5, this.loffset + this.plotwidth() + 5 - 0.5, h - ystop - 0.5, 'green', 1); //cs.macd[1]                        
                        _drawline(ctx, this.loffset - 5 - 0.5, h - ystop - 0.5, this.loffset + this.plotwidth() + 5 - 0.5, h - ystop - 0.5, cs.rsiLineColor, 0.5);
                        label = ystops[j];
                        this._drawText(label, r, h - ystop + 3, { align: 'left', color: cs.rsiLineColor, padding: 5, font: '7pt Arial' });
                    }

                    break;
                case 'adx':
                    //ymax = 100;
                    var d = _minmax2d(data.slice(begin, end)); // co dan hinh ve theo cac thoi diem
                    var ymax = d[1];
                    ymin = 0;
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.lineWidth = 0.5;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    //                    ctx.lineWidth = 0.5;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (data[j][0] === undefined) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);
                        //var adx = Math.round((data[j][0] - ymin) * scale);
                        //var pdi = Math.round((data[j][1] - ymin) * scale);
                        //var mdi = Math.round((data[j][2] - ymin) * scale);
                        var adx = (data[j][0] - ymin) * scale;
                        var pdi = (data[j][1] - ymin) * scale;
                        var mdi = (data[j][2] - ymin) * scale;
                        if (prevxy[0]) { //skip first line
                            _drawline(ctx, prevxy[0][0], prevxy[0][1], xline, h - adx, cs.bbands[1], 1); //cs.macd[2]
                            _drawline(ctx, prevxy[1][0], prevxy[1][1], xline, h - pdi, cs.adxcolor[0], 1); //cs.macd[0]
                            _drawline(ctx, prevxy[2][0], prevxy[2][1], xline, h - mdi, cs.adxcolor[1], 1); //cs.macd[1]
                        }
                        prevxy = [[xline, h - adx], [xline, h - pdi], [xline, h - mdi]];
                    }
                    this._drawText(str + ' = ' + Math.round(adx / scale * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] });
                    this._drawText(', +DI = ' + Math.round(pdi / scale * 100) / 100, l + 82, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.adxcolor[0] });
                    this._drawText(', -DI = ' + Math.round(mdi / scale * 100) / 100, l + 142, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.adxcolor[1] });

                    //ystops = [20,40,60];
                    //for (var j in ystops) {
                    //    var ystop = Math.round((ystops[j] - ymin) * scale);
                    //    _drawline(ctx, this.loffset, h - ystop, this.loffset + this.plotwidth, h - ystop, cs.stroke, 1);
                    //    label = ystops[j];
                    //    this._drawText(label, r, h - ystop, { align: 'left', padding: 5 });
                    //}
                    ystops = [pdi / scale, adx / scale, mdi / scale];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);

                        //_drawline(ctx, this.loffset, h - ystop, this.loffset + this.plotwidth, h - ystop, cs.stroke, 1);

                        label = Math.round(ystops[j] * 1000) / 1000;
                        if (j == 2) { this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.adxcolor[1], font: '8pt Arial' }); }
                        else if (j == 0) { this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.adxcolor[0], font: '8pt Arial' }); }
                        else { this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' }); }
                    }

                    ystops = this._indiHundred((ymin), (ymax), 5);
                    for (var i in ystops) {
                        var logystp = (ystops[i]);
                        var y1 = Math.round((logystp - ymin) * scale);
                        //if (y1 > 1) { // don't draw anything in first 20 pixels
                        _drawDashedLine(ctx, this.loffset - 5, h - y1, this.loffset + this.plotwidth() + 5, h - y1, cs.stroke, 0.14);   // ve duong ke ngang Price
                        _drawline(ctx, this.loffset + this.plotwidth(), h - y1, this.loffset + this.plotwidth() + 4, h - y1, cs.stroke, 0.5); // thang chia do
                        label = Math.round(ystops[i] * 10) / 10;
                        this._drawText(label, r, h - y1, { align: 'right', padding: 70, font: '8pt Arial' }); // label duong ke ngang Price
                        //}
                    };
                    break;
                case 'stoch':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    ymax = 100;
                    ymin = 0;
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (data[j][0] === undefined) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);
                        //var mhi = Math.round((data[j][0] - ymin) * scale);
                        //var mlo = Math.round((data[j][1] - ymin) * scale);
                        var mhi = (data[j][0] - ymin) * scale;
                        var mlo = (data[j][1] - ymin) * scale;
                        var mzero = Math.round((0 - ymin) * scale);

                        if (prevxy[0]) { //skip first line
                            _drawline(ctx, prevxy[0][0], prevxy[0][1], xline, h - mhi, 'blue', 1); // cs.macd[0], %d
                            _drawline(ctx, prevxy[1][0], prevxy[1][1], xline, h - mlo, 'red', 1); //cs.macd[1] %k                            
                        }
                        prevxy = [[xline, h - mhi], [xline, h - mlo]];
                    }

                    this._drawText('Stochastic %D = ' + Math.round(mhi / scale * 100) / 100, l, t + 10, { align: 'left', padding: 5, font: '8pt Arial', color: cs.adxcolor[0] });
                    this._drawText(', Stochastic %K = ' + Math.round(mlo / scale * 100) / 100, l + 120, t + 10, { align: 'left', padding: 5, font: '8pt Arial', color: cs.adxcolor[1] });

                    ystops = [20, 80, mhi / scale, mlo / scale];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);

                        if (j < 2) {
                            if (strBackground != 1) {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsiLineColor, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsiLineColor, 0.5);
                            }
                            else {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsiLineColor, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsiLineColor, 0.5);
                            }
                            label = ystops[j];
                            //nhathn khong cho hien thi gia tri tren cot ben phai                                                        
                            this._drawText(label, r, h - ystop + 3, { align: 'left', color: cs.rsiLineColor, padding: 5, font: '7pt Arial' });
                        }
                        else {
                            label = Math.round(ystops[j] * 1000) / 1000;
                            //nhathn khong cho hien thi gia tru tren cot ben phai
                            //this._drawText(label, r, h - ystop, { align: 'left', padding: 5, font: '8pt Arial', color: cs.adxcolor[j - 2] });
                        }

                    }
                    break;
                case 'uo':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax1d(data.slice(begin, end));
                    var ymax = d[1], ymin = 0;
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (!data[j]) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        var uo = (data[j] - ymin) * scale;
                        if (prevxy[0]) { //skip first line
                            if (strBackground == 1)
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - uo, cs.bbands[1], 1);
                            else
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - uo, cs.rcandle, 1);
                        }
                        prevxy = [xline, h - uo];
                    }
                    if (strBackground == 1)
                        this._drawText(str + ' = ' + Math.round(uo / scale * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] });
                    else
                        this._drawText(str + ' = ' + Math.round(uo / scale * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rcandle });



                    ystops = this._indiMomen((ymin), (ymax), 4);
                    for (var i in ystops) {
                        var logystp = (ystops[i]);
                        var y1 = Math.round((logystp - ymin) * scale);
                        //if (y1 > 1) { // don't draw anything in first 20 pixels
                        _drawDashedLine(ctx, this.loffset - 5, h - y1, this.loffset + this.plotwidth() + 5, h - y1, cs.stroke, 0.14);   // ve duong ke ngang Price
                        _drawline(ctx, this.loffset + this.plotwidth(), h - y1, this.loffset + this.plotwidth() + 4, h - y1, cs.stroke, 0.5); // thang chia do
                        label = Math.round(ystops[i] * 10) / 10;
                        this._drawText(label, r, h - y1, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' }); // label duong ke ngang Price
                        //}
                    };
                    ystops = [50, uo / scale]
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        if (j == 0) {
                            if (strBackground == 1) {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.colortext, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth(), h - ystop, this.loffset + this.plotwidth() + 4, h - ystop, cs.colortext, 0.5);
                            }
                            else {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                            }

                            label = ystops[j];
                        }
                        else {
                            label = Math.round(ystops[j] * 1000) / 1000;
                        }

                        if (j == 0) {
                            this._drawText(label, r, h - ystop, { align: 'right', padding: 50, font: '8pt Arial' });
                        }
                        else {
                            if (strBackground == 1)
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' });
                            else
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.rcandle, font: '8pt Arial' });
                        }
                    }
                    break;
                case 'momentum':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax1d(data.slice(begin, end));
                    var ymax = d[1], ymin = d[0];
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    for (var j = begin; j < end; j++) {
                        if (!data[j]) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        var momentum = (data[j] - ymin) * scale; // mod
                        if (prevxy[0]) { //skip first line                            
                            _drawline(ctx, prevxy[0], prevxy[1], xline, h - momentum, 'red', 1); // mod.                            
                        }
                        prevxy = [xline, h - momentum];  // mod
                    }
                    if (strBackground == 1)
                        this._drawText(str + ' = ' + Math.round((momentum / scale + ymin) * 100) / 100, l, t + 10, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] });
                    else
                        this._drawText(str + ' = ' + Math.round((momentum / scale + ymin) * 100) / 100, l, t + 10, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rcandle });

                    ystops = [100, momentum / scale + ymin];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        if (j != 1) {
                            if (strBackground != 1) {
                                //_drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                            }
                            else {
                                //_drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.colortext, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.colortext, 0.5);
                            }
                            label = ystops[j];
                        }
                        else {
                            //_drawDashedLine(ctx, this.loffset - 5, h - ystop, this.loffset + this.plotwidth() + 5, h - ystop, cs.colortext, 0.14);
                            //_drawline(ctx, this.loffset + this.plotwidth(), h - ystop, this.loffset + this.plotwidth() + 4, h - ystop, cs.colortext, 0.5);
                            label = Math.round(ystops[j] * 1000) / 1000;
                        }
                        //nhathn khong cho hien thi gia tri o cot ben phai
                        //if (j == 1) {
                        //    if (strBackground == 1)
                        //        this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' });
                        //    else
                        //        this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.rcandle, font: '8pt Arial' });
                        //}

                        //else {
                        //    this._drawText(label, r, h - ystop, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' });
                        //}
                    }
                    ystops = this._indiMomen((ymin), (ymax), 5);

                    for (var i in ystops) {
                        var logystp = (ystops[i]);
                        var y1 = Math.round((logystp - ymin) * scale);

                        _drawline(ctx, this.loffset - 5, h - y1, this.loffset + this.plotwidth() + 5, h - y1, cs.stroke, 0.25);   // ve duong ke ngang Price
                        //_drawline(ctx, this.loffset + this.plotwidth(), h - y1, this.loffset + this.plotwidth() + 4, h - y1, cs.stroke, 0.5); // thang chia do
                        label = Math.round(ystops[i] * 10) / 10;
                        this._drawText(label, r, h - y1 + 3, { align: 'left', padding: 5, font: '7pt Arial' }); // label duong ke 
                        //}
                    };
                    break;
                case 'mfi':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax1d(data.slice(begin, end));
                    var ymax = d[1], ymin = 0;
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (!data[j]) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        var mfi = (data[j] - ymin) * scale; // mod
                        if (prevxy[0]) { //skip first line
                            if (strBackground == 1)
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - mfi, cs.bbands[1], 1); // mod
                            else
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - mfi, cs.rcandle, 1); // mod
                        }
                        prevxy = [xline, h - mfi];  // mod
                    }
                    if (strBackground == 1)
                        this._drawText(str + ' = ' + Math.round(mfi / scale * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] });
                    else
                        this._drawText(str + ' = ' + Math.round(mfi / scale * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rcandle });

                    ystops = [20, 80, mfi / scale];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        if (j != 2) {
                            if (j == 0 && 30 > d[0]) {
                                if (strBackground == 1) {
                                    _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.colortext, 0.5);
                                    _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.colortext, 0.5);
                                }
                                else {
                                    _drawDashedLine(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                    _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                }
                                label = ystops[j];
                            }
                            else if (j == 1 && 70 < d[1]) {
                                if (strBackground == 1) {
                                    _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.colortext, 0.5);
                                    _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.colortext, 0.5);
                                }
                                else {
                                    _drawDashedLine(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                    _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                }
                                label = ystops[j];
                            }

                        }
                        else {
                            label = Math.round(ystops[j] * 1000) / 1000;
                        }
                        if (j == 2) {
                            if (strBackground == 1)
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' });
                            else
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.rcandle, font: '8pt Arial' });
                        }
                        else if (j == 0) { this._drawText(label, r, h - ystop, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' }); }
                        else {
                            if (j == 0 && 30 > d[0]) {
                                this._drawText(label, r, h - ystop, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' });
                            }
                            else if (j == 1 && 70 < d[1]) {
                                this._drawText(label, r, h - ystop, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' });
                            }
                        }
                    }
                    break;
                case 'cmf':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax1d(data.slice(begin, end));
                    var ymax = d[1], ymin = d[0];
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (!data[j]) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        var cmf = (data[j] - ymin) * scale; // mod
                        if (prevxy[0]) { //skip first line
                            if (strBackground == 1)
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - cmf, cs.bbands[1], 1); // mod
                            else
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - cmf, cs.rcandle, 1); // mod
                        }
                        prevxy = [xline, h - cmf];  // mod
                    }
                    if (strBackground == 1)
                        this._drawText(str + ' = ' + Math.round((cmf / scale + ymin) * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] });
                    else
                        this._drawText(str + ' = ' + Math.round((cmf / scale + ymin) * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rcandle });

                    ystops = [0, cmf / scale + ymin];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        if (j != 1) {
                            if (strBackground != 1) {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                            }
                            else {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.colortext, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.colortext, 0.5);
                            }
                            label = ystops[j];
                        }
                        else {
                            label = Math.round(ystops[j] * 1000) / 1000;
                        }
                        if (j == 1) {
                            if (strBackground == 1)
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' });
                            else
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.rcandle, font: '8pt Arial' });
                        }

                        else {
                            this._drawText(label, r, h - ystop, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' });
                        }
                    }
                    ystops = this._normalgrid((ymin), (ymax), 4);
                    for (var i in ystops) {
                        var logystp = (ystops[i]);
                        var y1 = Math.round((logystp - ymin) * scale);
                        //if (y1 > 1) { // don't draw anything in first 20 pixels
                        //_drawline(ctx, this.loffset - 5, h - y1, this.loffset + this.plotwidth() + 5, h - y1, cs.stroke, 0.1);   // ve duong ke ngang Price
                        _drawline(ctx, this.loffset + this.plotwidth(), h - y1, this.loffset + this.plotwidth() + 4, h - y1, cs.stroke, 0.5); // thang chia do
                        label = Math.round(ystops[i] * 10) / 10;
                        this._drawText(label, r, h - y1, { align: 'right', padding: 70, font: '8pt Arial' }); // label duong ke ngang Price
                        //}
                    };
                    break;
                case 'perR':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax1d(data.slice(begin, end));
                    var ymax = d[1], ymin = d[0];
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (!data[j]) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        var perR = (data[j] - ymin) * scale;
                        if (prevxy[0]) { //skip first line
                            if (strBackground == 1)
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - perR, cs.bbands[1], 1);
                            else // rcandle
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - perR, cs.rcandle, 1);
                        }
                        prevxy = [xline, h - perR];
                    }
                    if (strBackground == 1)
                        this._drawText(str + ' = ' + Math.round((perR / scale + ymin) * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] });
                    else // rcandle
                        this._drawText(str + ' = ' + Math.round((perR / scale + ymin) * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rcandle });

                    ystops = [-80, -20, perR / scale + ymin];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        if (j != 2) {
                            if (strBackground != 1) {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                            }
                            else {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.colortext, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.colortext, 0.5);
                            }

                            label = ystops[j];
                        }
                        else {
                            label = Math.round(ystops[j] * 1000) / 1000;
                        }
                        if (j == 2) {
                            if (strBackground == 1)
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' });
                            else
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.rcandle, font: '8pt Arial' });
                        }
                        else if (j == 0) { this._drawText(label, r, h - ystop, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' }); }
                        else { this._drawText(label, r, h - ystop, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' }); }
                    }
                    break;
                case 'obv':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax1d(data.slice(begin, end));
                    var ymax = d[1], ymin = d[0];
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (!data[j]) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        var obv = (data[j] - ymin) * scale;
                        if (prevxy[0]) { //skip first line
                            if (strBackground == 1)
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - obv, cs.bbands[1], 1);
                            else
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - obv, cs.rcandle, 1);
                        }
                        prevxy = [xline, h - obv];
                    }
                    if (strBackground == 1)
                        this._drawText(str + ' = ' + numberWithCommas(Math.round((obv / scale + ymin) * 100) / 100), l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] });
                    else
                        this._drawText(str + ' = ' + numberWithCommas(Math.round((obv / scale + ymin) * 100) / 100), l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rcandle });

                    ystops = [obv / scale + ymin];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        label = numberWithCommas(Math.round(ystops[j] * 1000) / 1000);

                        if (strBackground == 1)
                            this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' });
                        else
                            this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.rcandle, font: '8pt Arial' });
                    }

                    ystops = this._indigrid((ymin), (ymax), 2);
                    for (var i in ystops) {
                        var logystp = (ystops[i]);
                        var y1 = Math.round((logystp - ymin) * scale);
                        //if (y1 > 1) { // don't draw anything in first 20 pixels
                        _drawDashedLine(ctx, this.loffset, h - y1, this.loffset + this.plotwidth(), h - y1, cs.stroke, 0.14);   // ve duong ke ngang Price
                        _drawline(ctx, this.loffset + this.plotwidth(), h - y1, this.loffset + this.plotwidth() + 4, h - y1, cs.stroke, 0.5); // thang chia do
                        label = this._obvgrid(ystops[i], 1);
                        this._drawText(label, r, h - y1, { align: 'right', padding: 70, font: '8pt Arial' }); // label duong ke ngang Price
                        //}
                    };
                    // tam ko de vi co gia tri am ko hien thi dc rut gon
                    break;
                case 'atr':
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax1d(data.slice(begin, end));
                    var ymax = d[1], ymin = d[0];
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (!data[j]) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        var atr = (data[j] - ymin) * scale;

                        if (prevxy[0]) { //skip first line
                            if (strBackground == 1)
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - atr, cs.bbands[1], 1);
                            else
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - atr, cs.rcandle, 1);
                        }
                        prevxy = [xline, h - atr];
                    }
                    if (strBackground == 1)
                        this._drawText(str + ' = ' + Math.round((atr / scale + ymin) * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] });
                    else
                        this._drawText(str + ' = ' + Math.round((atr / scale + ymin) * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rcandle });

                    ystops = [atr / scale + ymin];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        label = numberWithCommas(Math.round(ystops[j] * 1000) / 1000);
                        if (strBackground == 1)
                            this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' });
                        else
                            this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.rcandle, font: '8pt Arial' });
                    }

                    ystops = this._normalgrid((ymin), (ymax), 3);
                    for (var i in ystops) {
                        var logystp = (ystops[i]);
                        var y1 = Math.round((logystp - ymin) * scale);
                        //if (y1 > 1) { // don't draw anything in first 20 pixels
                        _drawDashedLine(ctx, this.loffset - 5, h - y1, this.loffset + this.plotwidth() + 5, h - y1, cs.stroke, 0.14);   // ve duong ke ngang Price
                        _drawline(ctx, this.loffset + this.plotwidth(), h - y1, this.loffset + this.plotwidth() + 4, h - y1, cs.stroke, 0.5); // thang chia do
                        label = Math.round(ystops[i] * 10) / 10;
                        this._drawText(label, r, h - y1, { align: 'right', padding: 70, font: '8pt Arial' }); // label duong ke ngang Price
                        //}
                    };
                    break;
                case 'cci': // added huynq 26-05-2014
                    var z = document.getElementById("selectstyle");
                    var strBackground = z.options[z.selectedIndex].value;

                    var d = _minmax1d(data.slice(begin, end));
                    var ymax = d[1], ymin = d[0];
                    range = ymax - ymin;
                    var scale = (range == 0) ? 0 : this.liplotht / range;
                    var h = b;
                    prevxy = [];
                    //                    ctx.strokeStyle = this.cs.stroke;
                    //                    ctx.strokeRect(l - 5, t, this.plotwidth() + 5, this.liplotht + this.limargin);
                    for (var j = begin; j < end; j++) {
                        if (!data[j]) {
                            continue;
                        }
                        var i = j - begin;
                        var xlo = (c + i * cp.cwidth) - csize + this.loffset;
                        var xline = xlo + Math.round(csize / 2);

                        var cci = (data[j] - ymin) * scale;
                        if (prevxy[0]) { //skip first line
                            if (strBackground == 1)
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - cci, cs.bbands[1], 1);
                            else
                                _drawline(ctx, prevxy[0], prevxy[1], xline, h - cci, cs.rcandle, 1);
                        }
                        prevxy = [xline, h - cci];
                    }
                    if (strBackground == 1)
                        this._drawText(str + ' = ' + Math.round((cci / scale + ymin) * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.bbands[1] }); // added 19-04-2014 huynq
                    else
                        this._drawText(str + ' = ' + Math.round((cci / scale + ymin) * 100) / 100, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial', color: cs.rcandle }); // added 19-04-2014 huynq

                    ystops = [0, cci / scale + ymin];
                    for (var j in ystops) {
                        var ystop = Math.round((ystops[j] - ymin) * scale);
                        if (j != 1) {
                            if (strBackground != 1) {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.rsicolor, 0.5);
                            }
                            else {
                                _drawline(ctx, this.loffset - 5 - .5, h - ystop - .5, this.loffset + this.plotwidth() + 5 - .5, h - ystop - .5, cs.colortext, 0.5);
                                _drawline(ctx, this.loffset + this.plotwidth() - .5, h - ystop - .5, this.loffset + this.plotwidth() + 4 - .5, h - ystop - .5, cs.colortext, 0.5);
                            }
                            label = ystops[j];
                        }
                        else {
                            label = numberWithCommas(Math.round(ystops[j] * 1000) / 1000);
                        }
                        if (j == 1) {
                            if (strBackground == 1)
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.bbands[1], font: '8pt Arial' });
                            else
                                this._drawText(label, r, h - ystop, { align: 'left', padding: 5, color: cs.rcandle, font: '8pt Arial' });
                        }

                        else {
                            this._drawText(label, r, h - ystop, { align: 'right', padding: 70, color: cs.colortext, font: '8pt Arial' });
                        }
                    }

                    // added huynq 01-07-2014
                    ystops = this._normalgrid((ymin), (ymax), 4);
                    for (var i in ystops) {
                        var logystp = (ystops[i]);
                        var y1 = Math.round((logystp - ymin) * scale);
                        //if (y1 > 1) { // don't draw anything in first 20 pixels
                        _drawDashedLine(ctx, this.loffset - 5, h - y1, this.loffset + this.plotwidth() + 5, h - y1, cs.stroke, 0.14);   // ve duong ke ngang Price
                        _drawline(ctx, this.loffset + this.plotwidth(), h - y1, this.loffset + this.plotwidth() + 4, h - y1, cs.stroke, 0.5); // thang chia do
                        label = Math.round(ystops[i] * 10) / 10;
                        this._drawText(label, r, h - y1, { align: 'right', padding: 70, font: '8pt Arial' }); // label duong ke ngang Price
                        //}
                    };
                    break;
                default:
                    break;
            }
            //this._drawText(str, l, t + 20, { align: 'left', padding: 5, font: '8pt Arial' });

        },

        /* Bells and whistles functions : Gives you candles from X, Y 
        co-ordinate, if the mouse is in the area of a candle, candle
        is returned or else nothing. This is used by _showInfo to 
        display OHLC data. 
        */
        _getCandle: function (x, y) {
            x = x - this.loffset;
            y = y - this.topmargin;
            var cp = this.cp;

            var pc = this.plotwidth() / (cp.end - cp.begin);
            //var xos = Math.round(x / pc) + cp.begin;
            var xos = Math.round(x / pc) + cp.begin;
            if ((xos < cp.end) && (xos >= cp.begin)) {
                var candle = this.current.ohlc[xos];
                var chi = candle[1];
                var clo = candle[2];
                pc = this.plotht / (cp.ymax - cp.ymin);
                var yos = cp.ymax - Math.round(y / pc);
                //if((chi > yos) && (clo < yos)) { // remark huynq 29-05-2014
                return xos;
                //}
            }
            return null;
        },

        // Show information tooltip // huynq modified 29-05-2014
        // Modified huynq 20-06-2014: display tooltip for all indicator (IMPORTANT FIX)
        _showInfo: function (o, x, y) {

            var data = this.current.ohlc;
            var dataVol = this.current.vol;
            var dataDate = this.current.ts;
            var s = this.infodiv.style;
            /*
            for (var i in this.current.overlays) {
            if (this.current.overlays[i].type == 'sma')
            var data_sma = this.current.overlays[i].data;
            if (this.current.overlays[i].type == 'ema')
            var data_ema = this.current.overlays[i].data;
            if (this.current.overlays[i].type == 'psar')
            var data_psar = this.current.overlays[i].data;
            if (this.current.overlays[i].type == 'bbands') {
            var data_bbtop = this.current.overlays[i].tooltipbbtop;
            var data_bbmid = this.current.overlays[i].tooltipbbmid;
            var data_bbbot = this.current.overlays[i].tooltipbbbot;
            }
            }
            */
            /*
            for (var i in this.current.indicators) {
            if (this.current.indicators[i].str.substring(0, 3).toLowerCase() == 'rsi')
            var data_rsi = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'uo')
            var data_uo = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'momentum')
            var data_momentum = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'mfi')
            var data_mfi = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'cmf')
            var data_cmf = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'perR')
            var data_perR = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'obv')
            var data_obv = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'cci')
            var data_cci = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'atr')
            var data_atr = this.current.indicators[i].data;
            if (this.current.indicators[i].type == 'macd') {
            var data_macd = this.current.indicators[i].tooltipmacd;
            var data_macdsignal = this.current.indicators[i].tooltipsignal;
            }
            if (this.current.indicators[i].type == 'adx') {
            var data_adx = this.current.indicators[i].tooltipadx;
            var data_pDI = this.current.indicators[i].tooltippDI;
            var data_mDI = this.current.indicators[i].tooltipmDI;
            }
            if (this.current.indicators[i].type == 'stoch') {
            var data_pK = this.current.indicators[i].tooltipPK;
            var data_pD = this.current.indicators[i].tooltipPD;
            }
            }
            */
            s.cssText = this.cs.idcss;

            if (x + 125 > this.plotwidth()) x = this.plotwidth() - 125;
            if (y + infodiv > _getCanvas(this.width, this.height).height) y = _getCanvas(this.width, this.height).height - infodiv;
            if (y < 100) y = 100
            //s.left = (x) + 'px';            

            //s.left = (x) + 'px';
            //s.top = (y) + 'px';
            s.left = '0px'; //nhathn thong tin chi tiet se hien thi co dinh
            s.top = '0px';
            //s.height = infodiv + 'px'; // huynq modified 07-10-2014
            var dateStr = language == 'en-US' ? 'Date' : 'Ngày';
            var openStr = language == 'en-US' ? 'Open' : 'Mở cửa';
            var lowStr = language == 'en-US' ? 'Low' : 'Thấp nhất';
            var highStr = language == 'en-US' ? 'High' : 'Cao nhất';
            var closeStr = language == 'en-US' ? 'Close' : 'Đóng cửa';
            var volumeStr = language == 'en-US' ? 'Volume' : 'Khối lượng';
            html = '<table class="tblInfo">';
            html += "<tr> <td align=left>" + dateStr + ": " + _tsToDate(dataDate[o]) + "</td>";
            html += "<td align=left>" + openStr + ": " + data[o][0] + "</td>";
            html += "<td align=left>" + lowStr + ": " + data[o][2] + "</td>";
            html += "<td align=left>" + highStr + ": " + data[o][1] + "</td>";
            html += "<td align=left>" + closeStr + ": " + data[o][3] + "</td>";
            html += "<td align=left>" + volumeStr + ": " + numberWithCommas(dataVol[o]) + "</td></tr>";
            html += "</td>";
            html += '</table>';
            this.infodiv.innerHTML = html;
        },

        /* given PageX, PageY, find the offset on the canvas. This is of 
        importance to us to determine the candles later on
        *  FIXME : I think we can get rid of _canvasXXXX functions once the ElemPageOffsetXXX functions are there. That's for  first review. 
        */
        _canvasOffsetX: function (x, c) {
            var ox = 0;
            do {
                ox += c.offsetLeft;
            } while (c = c.offsetParent); // from quirksmode 
            return x - ox;
        },

        _canvasOffsetY: function (y, c) {
            var oy = 0;
            do {
                oy += c.offsetTop;
            } while (c = c.offsetParent); // from quirksmode 
            return y - oy;
        },

        // Fixme, one can move these out of the object
        _ElemPageOffsetX: function (e) {
            var ox = 0;
            do {
                ox += e.offsetLeft;
            } while (e = e.offsetParent); // from quirksmode 
            return ox;
        },

        _ElemPageOffsetY: function (e) {
            var oy = 0;
            do {
                oy += e.offsetTop;
            } while (e = e.offsetParent); // from quirksmode 
            return oy;
        },

        /* Text handling util functions */

        _drawText: function (txt, x, y, style) {
            var cs = this.cs;
            var ctx = this.ctx;
            var color = style.color || cs.label;
            var font = style.font || '10pt Arial bold';
            var padding = style.padding || 2;
            var align = style.align || 'start';

            ctx.font = font;
            ctx.textAlign = align;
            var l = x + 2 * padding + ctx.measureText(txt).width;
            ctx.fillStyle = this.cs.colortext;
            //FIXME : Fix for opera, check if it works 
            ctx.fillRect(x + padding - 1, y - 15, l, 0);
            ctx.fillStyle = color;
            ctx.fillText(txt, x + padding, y);

            return l;
        },

        _drawLegend: function (label, offset) {
            var y = 22 + this.tmargin;  // huynq modified 12-06-2014
            var x = offset * 100 + this.loffset;
            var color = this.cs.label; //this.cs.overlays[offset % this.cs.overlays.length]; // modified huynq 24-07-2014
            this._drawText(label, x, y, { padding: 0, color: color, font: '8pt Arial' });

        },

        _olexists: function (prop) {
            return (prop in this.current.overlays);
        },
        /* 
        TA functions: will be called in one of the following ways
        1. explicitely. (eg. Someone through UI ads an indicator) 
        2. Upon loading the original data, some indicators may be part of 
        'default' template. So the functions below get called.
        */

        /* 
        * ema: is a TA-API, and _ema is an internal API, some other indicators 
        like macd, use this. so it's better to keep them separate 
        */
        ema: function (data, period) {
            var prop = 'EMA(' + period + ')';

            if (this._olexists(prop)) {
                return;
            }
            var o = 3;
            var d = [];
            for (var i = 0; i < data.length; d.push(data[i++][o]));
            var e = this._ema(d, period);
            this.current.overlays[prop] = { data: e, offset: this.cs.oloffset, type: 'ema', strTitle: prop, str: prop };
            this.cs.oloffset += 1;
            this.cp.numoverlays += 1;

            return this;

        },
        _ema: function (data, period) {
            var e_ = [];
            var mult = 2.0 / (period + 1);

            e_[0] = data[0];
            // We should be able to handle sparse data. Also, data
            // that is undefined or null at the beginning
            for (var i = 1; i < data.length; i++) {
                if (i < period) {
                    e_[i] = undefined;
                    continue;
                }
                if (data[i] === undefined || data[i] === null) {
                    e_[i] = e_[i - 1];
                    continue;
                }
                if (e_[i - 1]) {
                    e_[i] = (data[i] * mult) + (1 - mult) * e_[i - 1];
                } else {
                    e_[i] = data[i];
                }
            }
            return e_;
        },

        // added huynq 26-05-2014 avedev = 1/n * SUM(abs(x(i) - xTB))
        _avedev: function (data, period) {
            var avg = this._sma(data, period);
            var s_ = [];
            period = period - 1;
            for (i = 0; i < data.length; i++) {
                if (i < period) {
                    s_[i] = undefined;
                    continue;
                }
                var t = data.slice(i - period, i + 1);
                var _sum = 0;
                for (j in t) {
                    _sum += Math.abs(t[j] - avg[i]);

                };
                s_[i] = 1 / (period + 1) * _sum;
            }
            return s_;
        },

        // added huynq 21-05-2014
        _sum: function (data, period) {
            var _sum = 0;
            var s_ = [];
            period = period - 1;
            for (i = 0; i < data.length; i++) {
                if (i < period) {
                    s_[i] = undefined;
                    continue;
                }
                var t = data.slice(i - period, i + 1);
                _sum = 0;
                for (j in t) {
                    _sum += t[j];

                };
                s_[i] = _sum;
            }
            return s_;
        },

        // added huynq 19-05-2014
        _sma: function (data, period) {
            var s_ = [];
            var _sum = 0;
            period = period - 1;
            for (i = 0; i < data.length; i++) {
                if (i < period) {
                    s_[i] = undefined;
                    continue;
                }
                var t = data.slice(i - period, i + 1);
                _sum = 0;
                for (j in t) {
                    _sum += t[j];

                };
                s_[i] = _sum / (period + 1);
            }
            return s_;
        },

        /* simple moving average : really simple */
        sma: function (data, period) {
            var o = 3;

            var prop = 'SMA(' + period + ')';
            if (this._olexists(prop)) {
                return;
            }
            var s_ = [];
            var _sum = 0;
            period = period - 1;
            for (i = 0; i < data.length; i++) {
                if (i < period) {
                    s_[i] = undefined;
                    continue;
                }
                var t = data.slice(i - period, i + 1);
                _sum = 0;
                for (j in t) {
                    _sum += t[j][o];

                };
                s_[i] = _sum / (period + 1);
            }
            this.current.overlays[prop] = { data: s_, offset: this.cs.oloffset, type: 'sma', strTitle: prop, str: prop };
            this.cs.oloffset += 1;
            this.cp.numoverlays += 1;
            return this;
        },

        /* parabolic SAR  
        psar: function(data, af, maxaf) {
        var i = 0, UP = 1, DOWN = 2;
        var currtrend = UP;
        var curraf = af;
        var updated = false;
        var d;
        var trendmin, trendmax;
        var prop = 'psar' + af + '-' + maxaf;

            if (this._olexists(prop)) {
        return;
        } 
        var p_ = [];
        for (i in data) {
        d = data[i];
        j = parseInt(i);
        if (i == 0) {
        p_[j+1] = d[2]; p_[j] = d[2];
        trendmin = d[2];
        trendmax = d[1];
        continue;
        }
        if (currtrend == UP) { 
        if(d[1] > trendmax) { 
        trendmax = d[1];
        p_[j+1] = p_[j] + curraf*(trendmax - p_[j]);
        curraf = curraf + af;
        updated = true;
        }
        if (d[2] < p_[j]) { 
        p_[j] = trendmax;
        p_[j+1] = trendmax;
        curraf = af; 
        currtrend = DOWN;
        trendmin = d[2];
        trendmax = d[1];
        updated = true;
        } 
        } 
        if (currtrend == DOWN) { 
        if(d[2] < trendmin) { 
        trendmin = d[2];
        p_[j+1] = p_[j] + curraf*(trendmin - p_[j]); 
        curraf = curraf + af;
        updated = true;
        }
        if (d[1] > p_[j]) { 
        p_[j] = trendmin;
        p_[j+1] = trendmin;
        curraf = af; 
        currtrend = UP;
        trendmin = d[2];
        trendmax = d[1];
        updated = true;
        } 
        } 
        if (! updated) { 
        if(currtrend == UP) 
        p_[j+1] = p_[j] + curraf*(trendmax - p_[j]); 
        else 
        p_[j+1] = p_[j] + curraf*(trendmin - p_[j]); 
        } 
        updated = false;
        if (curraf > maxaf) {curraf = maxaf;}
        }
        this.current.overlays[prop] = {data: p_ };
        this.cp.numoverlays +=1 ;
        return this;
        },
        */

        psar: function (data, af, maxaf) {
            var i = 0;
            var longs;
            var ep, hp, lp;
            var reverse;

            var curraf = af;    // init acelleration factor
            var prop = 'psar(' + af + ',' + maxaf + ')';

            if (this._olexists(prop)) {
                return;
            }
            var p_ = [];
            for (i in data) {
                j = parseInt(i);
                if (j < 2) {
                    p_[j] = data[j][3];     // initialize
                    longs = 1;              // assume long for initial conditions
                    hp = Math.max(data[0][1], data[1][1]);
                    lp = Math.min(data[0][2], data[1][2]);
                    continue;
                }
                if (longs) {
                    p_[j] = p_[j - 1] + curraf * (hp - p_[j - 1]);
                }
                else {
                    p_[j] = p_[j - 1] + curraf * (lp - p_[j - 1]);
                }
                reverse = 0;
                //check for reversal
                if (longs) {
                    if (data[j][2] < p_[j]) {
                        longs = 0; reverse = 1; // reverse position to Short
                        p_[j] = hp;             // SAR is High point in prev trade
                        lp = data[j][2];
                        curraf = af;
                    }
                }
                else {
                    if (data[j][1] > p_[j]) {
                        longs = 1; reverse = 1; //reverse position to long
                        p_[j] = lp;
                        hp = data[j][1];
                        curraf = af;
                    }
                }

                if (reverse == 0) {
                    if (longs) {
                        if (data[j][1] > hp) {
                            hp = data[j][1];
                            curraf = curraf + af;
                            if (curraf > maxaf) curraf = maxaf;
                        }

                        if (data[j - 1][2] < p_[j]) p_[j] = data[j - 1][2];
                        if (data[j - 2][2] < p_[j]) p_[j] = data[j - 2][2];
                    }
                    else {
                        if (data[j][2] < lp) {
                            lp = data[j][2];
                            curraf = curraf + af;
                            if (curraf > maxaf) curraf = maxaf;
                        }
                        if (data[j - 1][1] > p_[j]) p_[j] = data[j - 1][1];
                        if (data[j - 2][1] > p_[j]) p_[j] = data[j - 2][1];
                    }
                }
            }
            this.current.overlays[prop] = { data: p_, offset: this.cs.oloffset, type: 'psar', strTitle: prop, str: 'SAR(' + af + ',' + maxaf + ')' };
            this.cp.numoverlays += 1;
            this.cs.oloffset += 1;
            return this;
        },

        bbands: function (data, period, mult) {

            var b_ = [], bbandtop = [], bbandmid = [], bbandbot = [];
            var prop = 'bbands(' + period + ',' + mult + ')';

            if (this._olexists(prop)) {
                return;
            }
            period = period - 1;
            for (var i = 0; i < data.length; i++) {
                if (i < period) {
                    b_[i] = [undefined, undefined, undefined];
                    continue;
                }
                var t = data.slice(i - period, i + 1);
                var tc = [];
                var _s = 0;
                for (j in t) {
                    _s = _s + t[j][3];
                    tc.push(t[j][3]);
                }
                var sigma = stats.pstdev(tc);
                var mu = _s / (period + 1);
                bbandtop[i] = (mu + mult * sigma);
                bbandmid[i] = mu;
                bbandbot[i] = (mu - mult * sigma);
                b_[i] = [(mu + mult * sigma), mu, (mu - mult * sigma)];
            }
            this.current.overlays[prop] = { data: b_, offset: this.cs.oloffset, type: 'bbands', tooltipbbtop: bbandtop, tooltipbbmid: bbandmid, tooltipbbbot: bbandbot, strTitle: prop, str: 'BBAND(' + parseInt(period + 1) + ',' + mult + ')' }; // modified 20-06-2014 huynq add tooltip
            this.cp.numoverlays += 1;
            //this.cs.oloffset += 1;
            return this;
        },

        /* p1 : is a faster Moving average (numerically lower) 
        p2 : is a slower Moving average (numerically higher)
        signal : is ema signal of p1 - p2 
        
        macd: function(data, p1, p2, signal) {
        var istr = 'MACD(' + p1 + ', ' + p2 + ', ' + signal + ')';
        for(var i in this.current.indicators) { 
        if (this.current.indicators[i].str == istr)
        return;
        } 
        var d = [];
        for(var i = 0; i < data.length; d.push(data[i++][3]));
        var ep1 = this._ema(d, p1);   
        var ep2 = this._ema(d, p2);   
        for(var i = 0; i < ep1.length; i++) { 
        ep1[i] = ep1[i] - ep2[i];
        } 
        ep2 = this._ema(ep1, signal);
        var m_ = [];
        for(i = 0; i < ep1.length; i++) { 
        m_[i] = [ep1[i], ep2[i], (ep1[i] - ep2[i])]; 
        }
        var i = {type : 'macd', data:m_, str: istr};
        this.current.indicators.push(i);
        this.cp.numindicators += 1;
        return this;
        },
        */

        macd: function (data, p1, p2, signal) {
            var istr = 'MACD(' + p1 + ', ' + p2 + ', ' + signal + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }
            var d = [];
            for (var i = 0; i < data.length; d.push(data[i++][3])); // get close
            var ema1 = [], ema2 = [], macd = [], signalMACD = [];
            ema1[0] = d[0];
            ema2[0] = d[0];
            macd[0] = 0;
            signalMACD[0] = 0;
            for (var i = 1; i < d.length; i++) {
                ema1[i] = (2 / (p1 + 1)) * d[i] + (1 - (2 / (p1 + 1))) * ema1[i - 1];
                ema2[i] = (2 / (p2 + 1)) * d[i] + (1 - (2 / (p2 + 1))) * ema2[i - 1];
                macd[i] = ema1[i] - ema2[i];
                signalMACD[i] = (macd[i] == 0) ? 0 : (2 / (signal + 1)) * macd[i] + (1 - (2 / (signal + 1))) * signalMACD[i - 1];
            }
            var m_ = [];
            for (i = 0; i < d.length; i++) {
                m_[i] = [macd[i], signalMACD[i], (macd[i] - signalMACD[i])];
            }

            var i = { type: 'macd', data: m_, tooltipmacd: macd, tooltipsignal: signalMACD, str: istr };
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
            return this;
        },

        /*
        rsi follow formular of stockchart
        rsi: function(data, lookback) {
        var up = 0, down = 0;
        var rs; 
    
            var istr = 'RSI(' +  lookback + ')';
        for(var i in this.current.indicators) { 
        if (this.current.indicators[i].str == istr)
        return;
        } 

            var rsi = [undefined]; // empty array plus initialization for 0.
        var prev = data[0][3];
        if (lookback > data.length) lookback = data.length;
        for(var i = 1; i < lookback; i++) { 
        var diff = data[i][3] - prev;
        if (diff > 0 ) { 
        up = up + diff;
        } else { 
        down = down - diff;
        } 
        rsi.push(undefined);
        prev = data[i][3];
        }
        up /= lookback;
        down /= lookback;
        rs = up/down;
        for (var i = lookback; i < data.length; i++) { 
        var diff = data[i][3] - prev;
        rsi[i] = 100 - 100/(1+rs); 
        if(diff >= 0) {
        up = (up*(lookback-1)+diff)/lookback;
        down = down*(lookback-1)/lookback;
        } else { 
        down = (down*(lookback-1)-diff)/lookback;
        up = up*(lookback-1)/lookback;
        }; 
        rs = up/down;
        prev = data[i][3];
        }

            var i = {type : 'rsi', data:rsi, str: istr} 
        this.current.indicators.push(i);
        this.cp.numindicators += 1;
        },
        */


        rsi: function (data, lookback) {
            var up = 0, down = 0;
            var rs;
            var istr = 'StochRSI(' + lookback + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }
            var rsi = [undefined]; // empty array plus initialization for 0.
            var stochrsi = [undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]; // empty array plus initialization for 0.            
            var prev = data[0][3];  // close
            if (lookback > data.length) lookback = data.length;
            for (var i = 1; i < lookback; i++) {//huynq modified 10-10-2014
                var diff = data[i][3] - prev;  // change                
                if (diff > 0) {
                    up = up + diff; // Sumgain
                } else {
                    down = down - diff;  // Sumloss
                }
                rsi.push(undefined);
                prev = data[i][3];
            }
            up /= lookback; // avg Gain
            down /= lookback; // avg Loss            
            rs = (down == 0) ? 0 : (up / down); // RS		// modified huynq 14-10-2014
            for (var i = lookback; i < data.length; i++) {
                var diff = data[i][3] - prev;
                if (i != lookback) {
                    if (diff > 0) {
                        up = (up * (lookback - 1) + diff) / lookback;
                        down = down * (lookback - 1) / lookback;
                    } else {
                        down = (down * (lookback - 1) - diff) / lookback;
                        up = up * (lookback - 1) / lookback;
                    };
                }
                rs = (down == 0) ? 0 : (up / down); // RS		// modified huynq 14-10-2014                
                var rsiCurrent = 100 - 100 / (1 + rs);
                rsi[i] = rsiCurrent;
                prev = data[i][3];
                if (i >= (lookback * 2 - 1)) {
                    var rsRange = rsi.slice(i - lookback + 1);
                    var maxRSI = Math.max.apply(null, rsRange);
                    var minRSI = Math.min.apply(null, rsRange);
                    stochrsi[i] = (rsiCurrent - minRSI) / (maxRSI - minRSI);
                }
            }
            var i = { type: 'rsi', data: stochrsi, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },

        // added nqhuy 14-05-2014
        /*
        adx: function (data, lookback) {
        var pDM14 = 0, mDM14 = 0;
        var tr14 = 0;

            var istr = 'ADX(' + lookback + ')';
        for (var i in this.current.indicators) {
        if (this.current.indicators[i].str == istr)
        return;
        }

            var adx = [undefined]; // empty array plus initialization for 0.
        var pDIN = [], mDIN = [];

            var prevC = data[0][3];  // close
        var prevH = data[0][1];  // high
        var prevL = data[0][2];  // low
        if (lookback > data.length) lookback = data.length;
        for (var i = 1; i <= lookback; i++) {
        tr14 += Math.max(data[i][1] - data[i][2], Math.abs(data[i][1] - prevC), Math.abs(data[i][2] - prevC));  // tr
        if (data[i][1] - prevH > prevL - data[i][2]) {
        pDM14 += Math.max(data[i][1] - prevH, 0);
        }
        else {
        pDM14 += 0;
        }
        if (prevL - data[i][2] > data[i][1] - prevH) {
        mDM14 += Math.max(prevL - data[i][2], 0);
        }
        else {
        mDM14 += 0;
        }
        adx.push(undefined);
        pDIN.push(undefined);
        mDIN.push(undefined);
        prevC = data[i][3];
        prevH = data[i][1];
        prevL = data[i][2];
        }
        var pDI14 = 0, mDI14 = 0, DI14Diff = 0, DI14Sum = 0, DX = 0;

            for (var i = lookback; i < data.length; i++) {
        tr14 = tr14 - (tr14 / lookback) + Math.max(data[i][1] - data[i][2], Math.abs(data[i][1] - prevC), Math.abs(data[i][2] - prevC));  // tr
        pDM14 = pDM14 - (pDM14 / lookback) + Math.max(data[i][1] - prevH, 0);
        mDM14 = mDM14 - (mDM14 / lookback) + Math.max(prevL - data[i][2], 0);

                pDI14 = 100 * (pDM14 / tr14);
        mDI14 = 100 * (mDM14 / tr14);
        DI14Diff = Math.abs(pDI14 - mDI14);
        DI14Sum = pDI14 + mDI14;
        DX = 100 * (DI14Diff / DI14Sum);
        adx.push(DX);
        pDIN.push(pDI14);
        mDIN.push(mDI14);

                prevC = data[i][3];
        prevH = data[i][1];
        prevL = data[i][2];
        }
        var adxResult = [];
        var adxSmooth = this._ema(adx, lookback);
        for (i = 0; i < pDIN.length; i++) {
        adxResult[i] = [adxSmooth[i], pDIN[i], mDIN[i]];
        }

        var i = { type: 'adx', data: adxResult, tooltipadx: adxSmooth, tooltippDI: pDIN, tooltipmDI: mDIN, str: istr }
        this.current.indicators.push(i);
        this.cp.numindicators += 1;
        },
        */

        // modified nqhuy 18-05-2014
        adx: function (data, lookback) {
            var pDM14 = 0, mDM14 = 0;
            var tr14 = 0;

            var istr = 'ADX(' + lookback + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }

            var adx = [], pDIN = [], mDIN = [];

            var prevC = data[0][3];  // close
            var prevH = data[0][1];  // high
            var prevL = data[0][2];  // low

            if (lookback > data.length) lookback = data.length;
            for (var i = 1; i <= lookback; i++) {
                tr14 += Math.max(data[i][1] - data[i][2], Math.abs(data[i][1] - prevC), Math.abs(data[i][2] - prevC));  // tr
                if ((data[i][1] > prevH) && (data[i][2] >= prevL)) {
                    pDM14 += data[i][1] - prevH;
                }
                else if ((data[i][1] > prevH) && (data[i][2] < prevL) && ((data[i][1] - prevH) > (prevL - data[i][2]))) {
                    pDM14 += data[i][1] - prevH;
                }
                else {
                    pDM14 += 0;
                }
                if ((data[i][1] <= prevH) && (data[i][2] < prevL)) {
                    mDM14 += prevL - data[i][2];
                }
                else if ((data[i][1] > prevH) && (data[i][2] < prevL) && ((data[i][1] - prevH) < (prevL - data[i][2]))) {
                    mDM14 += prevL - data[i][2];
                }
                else {
                    mDM14 += 0;
                }
                adx.push(undefined);
                pDIN.push(undefined);
                mDIN.push(undefined);
                prevC = data[i][3];
                prevH = data[i][1];
                prevL = data[i][2];
            }

            var pDI14 = 0, mDI14 = 0, DI14Diff = 0, DI14Sum = 0, DX = 0, adx_temp = 0;


            for (var i = lookback; i < data.length; i++) {
                if (i != lookback) {
                    tr14 = tr14 - (tr14 / lookback) + Math.max(data[i][1] - data[i][2], Math.abs(data[i][1] - prevC), Math.abs(data[i][2] - prevC));  // tr
                    if ((data[i][1] > prevH) && (data[i][2] >= prevL)) {
                        pDM14 = pDM14 - (pDM14 / lookback) + data[i][1] - prevH;
                    }
                    else if ((data[i][1] > prevH) && (data[i][2] < prevL) && ((data[i][1] - prevH) > (prevL - data[i][2]))) {
                        pDM14 = pDM14 - (pDM14 / lookback) + data[i][1] - prevH;
                    }
                    else {
                        pDM14 = pDM14 - (pDM14 / lookback);
                    }
                    if ((data[i][1] <= prevH) && (data[i][2] < prevL)) {
                        mDM14 = mDM14 - (mDM14 / lookback) + prevL - data[i][2];
                    }
                    else if ((data[i][1] > prevH) && (data[i][2] < prevL) && ((data[i][1] - prevH) < (prevL - data[i][2]))) {
                        mDM14 = mDM14 - (mDM14 / lookback) + prevL - data[i][2];
                    }
                    else {
                        mDM14 = mDM14 - (mDM14 / lookback);
                    }
                    //                    if (data[i][1] - prevH > prevL - data[i][2]) {
                    //                        pDM14 = pDM14 - (pDM14 / lookback) + Math.max(data[i][1] - prevH, 0);
                    //                    }
                    //                    else {
                    //                        pDM14 = pDM14 - (pDM14 / lookback);
                    //                    }
                    //                    if (prevL - data[i][2] > data[i][1] - prevH) {
                    //                        mDM14 = mDM14 - (mDM14 / lookback) + Math.max(prevL - data[i][2], 0);
                    //                    }
                    //                    else {
                    //                        mDM14 = mDM14 - (mDM14 / lookback);
                    //                    }

                }
                pDI14 = 100 * (pDM14 / tr14);
                mDI14 = 100 * (mDM14 / tr14);
                DI14Diff = Math.abs(pDI14 - mDI14);
                DI14Sum = pDI14 + mDI14;
                DX = 100 * (DI14Diff / DI14Sum);
                if (i <= lookback * 2 - 1) { adx_temp += DX; }
                adx.push(DX);
                pDIN.push(pDI14);
                mDIN.push(mDI14);

                prevC = data[i][3];
                prevH = data[i][1];
                prevL = data[i][2];
            }
            var adxResult = [];
            var adxSmooth = [];
            for (var i = 2 * lookback - 1; i < data.length; i++) {
                if (i == 2 * lookback - 1) {
                    adxSmooth[i] = adx_temp / lookback;
                }
                else {
                    adxSmooth[i] = ((lookback - 1) * adxSmooth[i - 1] + adx[i]) / lookback;
                }
            }

            //var adxSmooth= this._ema(adx,lookback);
            for (i = 0; i < data.length; i++) {
                adxResult[i] = [adxSmooth[i], pDIN[i], mDIN[i]];
            }
            //adxResult = this._ema(adx,lookback);

            var i = { type: 'adx', data: adxResult, tooltipadx: adxSmooth, tooltippDI: pDIN, tooltipmDI: mDIN, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },

        /*
        stoch: function(data, k, x, d) { 
        var min, max, pk, d_;
        var istr = 'STOCH( ' + k + ', ' + x + ', ' + d + ')';
        for(var i in this.current.indicators) { 
        if (this.current.indicators[i].str == istr)
        return;
        } 
        k = k -1 ;
        pk = [];
        for(i = 0; i < k; pk[i++] = undefined); 
        for(var i = k; i < data.length; i++) { 
        d_ = _minmax2d(data.slice(i-k, i+1));
        min = d_[0]; max = d_[1];

                pk[i] = (data[i][3] - min)/(max - min) * 100;
        }
        var pk_ = this._ema(pk,x);
        var pd_ = this._ema(pk_,x);

            for(i = 0;i < data.length; i++) { 
        pk_[i] = [pk_[i] , pd_[i]];
        }
        var i = {type : 'stoch', data:pk_, str: istr};
        this.current.indicators.push(i);
        this.cp.numindicators += 1;
        }
        */
        stoch: function (data, k, x, d) {
            var min, max, pk, d_;
            var istr = 'STOCH(' + k + ', ' + x + ', ' + d + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }
            k = k - 1;
            pk = []; // %K fast
            var HHV = [], LLV = [];
            for (i = 0; i < k; pk[i++] = undefined, HHV[i++] = undefined, LLV[i++] = undefined);
            for (var i = k; i < data.length; i++) {
                d_ = _minmax2d(data.slice(i - k, i + 1));
                min = d_[0]; max = d_[1];

                pk[i] = (data[i][3] - min) / (max - min) * 100;
            }
            //var pk_ = this._ema(pk,x);
            //var pd_ = this._ema(pk_,x);
            var pd_ = this._sma(pk, x); // %D fast

            var pks_ = [], pds_ = [], pk_ = [];
            pks_ = this._sma(pk, d); 	// %K slow
            pds_ = this._sma(pks_, d); // %D slow
            for (i = 0; i < data.length; i++) {
                pk_[i] = [pks_[i], pds_[i]];
            }
            var i = { type: 'stoch', data: pk_, tooltipPK: pks_, tooltipPD: pds_, str: istr };
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },


        uo: function (data, fastavg, medavg, slowavg) {
            var istr = 'Ultimate(' + fastavg + ', ' + medavg + ', ' + slowavg + ')'; //// de the sau se bi loi khi add lai ko hieu %R la type indicator j
            //var istr = 'uo(' + fastavg + ', ' + medavg + ', ' + slowavg + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }

            var prevC = data[0][3];
            var bp = [], tr = [];
            for (var i = 1; i < data.length; i++) {
                bp[i] = data[i][3] - Math.min(data[i][2], prevC);
                tr[i] = Math.max(data[i][1], prevC) - Math.min(data[i][2], prevC);

                prevC = data[i][3];
            }

            var avg7 = [], avg14 = [], avg28 = [], uo = [];
            var sumbpfast = this._sum(bp, fastavg);
            var sumtrfast = this._sum(tr, fastavg);
            var sumbpmed = this._sum(bp, medavg);
            var sumtrmed = this._sum(tr, medavg);
            var sumbpslow = this._sum(bp, slowavg);
            var sumtrslow = this._sum(tr, slowavg);
            for (var i = fastavg; i < data.length; i++) {
                if (i >= fastavg) { avg7[i] = sumbpfast[i] / sumtrfast[i]; }
                if (i >= medavg) { avg14[i] = sumbpmed[i] / sumtrmed[i]; }
                if (i >= slowavg) {
                    avg28[i] = sumbpslow[i] / sumtrslow[i];
                    uo[i] = 100 * (4 * avg7[i] + 2 * avg14[i] + avg28[i]) / 7;
                }
            }

            var i = { type: 'uo', data: uo, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },


        momentum: function (data, lookback) {
            var istr = 'ROC(' + lookback + ')'; //nhathn thay momentum = roc
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }
            var momentum = [];
            for (var i = lookback; i < data.length; i++) {
                if (data[i - lookback][3] == 0)
                    momentum[i] = 0;
                else //tinh lai roc                    
                    momentum[i] = (data[i - lookback][3] == 0) ? 0 : (100 * (data[i][3] - data[i - lookback][3]) / (data[i - lookback][3]));
            }
            var i = { type: 'momentum', data: momentum, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },

        mfi: function (data, vol, lookback) {
            var istr = 'MFI(' + lookback + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }

            var typicalPrice = [], updown = [], rawMF = [], positive = [], negative = [];
            typicalPrice[0] = (data[0][1] + data[0][2] + data[0][3]) / 3;

            for (var i = 1; i < data.length; i++) {
                typicalPrice[i] = (data[i][1] + data[i][2] + data[i][3]) / 3;
                updown[i] = ((typicalPrice[i] - typicalPrice[i - 1]) > 0) ? 1 : (((typicalPrice[i] - typicalPrice[i - 1]) < 0) ? -1 : 0); // modified huynq 26-06-2014
                rawMF[i] = vol[i] * typicalPrice[i];
                positive[i] = (updown[i] == 1) ? rawMF[i] : 0;
                negative[i] = (updown[i] == -1) ? rawMF[i] : 0;
            }

            var sumPos = [], sumNeg = [], mfi = [];
            sumPos = this._sum(positive, lookback);
            sumNeg = this._sum(negative, lookback);

            for (var i = lookback; i < data.length; i++) {
                mfi[i] = 100 - 100 / (1 + sumPos[i] / sumNeg[i]);
            }

            var i = { type: 'mfi', data: mfi, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },

        cmf: function (data, vol, lookback) {
            var istr = 'CMF(' + lookback + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }

            var ratio = [];

            for (var i = 0; i < data.length; i++) {
                ratio[i] = (data[i][1] - data[i][2] == 0) ? 0 : ((2 * data[i][3] - data[i][1] - data[i][2]) / (data[i][1] - data[i][2])) * vol[i];
            }

            var sumRatio = [], sumVol = [], cmf = [];
            sumRatio = this._sum(ratio, lookback);
            sumVol = this._sum(vol, lookback);

            for (var i = lookback - 1; i < data.length; i++) {
                cmf[i] = sumRatio[i] / sumVol[i];
            }

            var i = { type: 'cmf', data: cmf, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },

        perR: function (data, lookback) {
            var istr = '%R(' + lookback + ')'; // de the sau se bi loi khi add lai ko hieu %R la type indicator j
            //var istr = 'perR(' + lookback + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }

            var perR = [], min, max, d_;
            for (var i = lookback - 1; i < data.length; i++) {
                d_ = _minmax2d(data.slice(i - lookback + 1, i + 1));
                min = d_[0]; max = d_[1];

                perR[i] = (max - data[i][3]) / (max - min) * -100;
            }

            var i = { type: 'perR', data: perR, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },

        obv: function (data, vol) {
            var istr = 'OBV';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }

            var obv = [], prevC;
            obv[0] = vol[0];
            prevC = data[0][3];

            for (var i = 1; i < data.length; i++) {
                obv[i] = ((data[i][3] == prevC) ? 0 : ((data[i][3] > prevC) ? 1 : -1)) * vol[i] + obv[i - 1];
                prevC = data[i][3];
            }

            var i = { type: 'obv', data: obv, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },

        atr: function (data, lookback) {
            var istr = 'ATR(' + lookback + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }

            var prevC = data[0][3];
            var tr = [];
            tr[0] = data[0][1] - data[0][2];
            for (var i = 1; i < data.length; i++) {
                tr[i] = Math.max(data[i][1] - data[i][2], Math.abs(data[i][1] - prevC), Math.abs(data[i][2] - prevC));

                prevC = data[i][3];
            }

            var atr = [];
            var sumtr = this._sum(tr, lookback);
            for (var i = lookback - 1; i < data.length; i++) {
                if (i == lookback - 1) {
                    atr[lookback - 1] = sumtr[lookback - 1] / lookback;
                }
                else {
                    atr[i] = (atr[i - 1] * (lookback - 1) + tr[i]) / lookback;
                }
            }

            var i = { type: 'atr', data: atr, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        },

        cci: function (data, lookback) {
            var istr = 'CCI(' + lookback + ')';
            for (var i in this.current.indicators) {
                if (this.current.indicators[i].str == istr)
                    return;
            }

            var cci = [], tp = [], smatp = [], avedev = [];

            for (var i = 0; i < data.length; i++) {
                tp[i] = (data[i][1] + data[i][2] + data[i][3]) / 3;
            }
            smatp = this._sma(tp, lookback);
            avedev = this._avedev(tp, lookback);
            for (var i = lookback - 1; i < data.length; i++) {
                cci[i] = (tp[i] - smatp[i]) / (0.015 * avedev[i]);
            }

            var i = { type: 'cci', data: cci, str: istr }
            this.current.indicators.push(i);
            this.cp.numindicators += 1;
        }

    };
    /* below is needed to give the object all the methods reqd. */
    $.tickp.fn.init.prototype = $.tickp.fn;

    /* the chartparams object. Used by several plot routines to findout current
    chart settings. Used for plotting */
    $.tickp.chartparams = {
        init: function (plot) {
            this.plot = plot; // have a ref back to plot 
            return this;
        },
        logscale: false,
        autoscale: false,
        type: 1, // 1: candlestick, 2: ohlc, 3: linecharts, 4: heikin-ashi, 5: candlestick meta style
        w: undefined,
        h: undefined,
        candles: undefined,
        cwidth: undefined,
        csize: undefined,
        ymin: undefined,
        ymax: undefined,
        numoverlays: 0, // number of overlays.
        numindicators: 0, // number of overlays.
        maxylabels: 6, // so luong ke ngang
        maxxlabels: 6,  // so luong thanh ke doc // modified huynq 21-05-2014
        minwidth: 8, //minimum width of a candle
        xmin: 0,
        zt: 3 //zoomtype
        //enddate: undefined
    };
    /* assigning prototype */
    $.tickp.chartparams.init.prototype = $.tickp.chartparams;

    $.tickp.csdark = {
        //background: '#222222', //'#000000', 
        background: '#000000',
        label: '#EEEEEE',
        stroke: '#AAAAAA',
        gridlines: '#AAAAAA',
        overlays: ['yellow', '#5217DB', '#18E197', '#DED71F', '#DE521F', '#10F5B8', '#A6ACE2', '#DF9FB0'], //['#FF6600', '#FFFF33', '#FFFF33', '#00CCFF', '#3366FF'],

        bbands: ['#aabbcc', '#FF9C00', '#aabbcc'],
        macd: ['#0000FF', '#FF0000', '#8B008B'], // modified macd[2] for adx color        
        psar: '#CCFFFF',
        rcandle: '#FF0000',
        gcandle: '#008000',
        bodycandle: '#222000', // added huynq 30-05-2014 candlestick style metastock
        lineplot: '#CCCCCC',
        //idcss: 'position:absolute; opacity: 0.85;z-index:1;pointer-events: none; border: 1px solid #0066CC; background: #EEE;font-size:10px;font-family:arial,sans-serif;text-align:right; padding:2px;',  //modified width, height nqhuy 13-05-2014
        idcss: 'position:absolute; opacity: 0.85;z-index:1;pointer-events: none;font-size:10px;font-family:arial,sans-serif;text-align:right;',  //modified width, height nqhuy 13-05-2014
        //-webkit-transition-delay:0.5s;-moz-transition-delay:0.5s; -ms-transition-delay:0.5s;-o-transition-delay:0.5s;transition-delay:0.5s;
        oloffset: 0,
        colortext: '#C0C0C0',
        colorover: '#C0C0C0', //nhathn
        adxcolor: ['#008000', '#FF0000'],
        rsiLineColor: '#07E8C9', //mau cua cac duong rsi70,rsi30,so80,so
        rsiMainColor: 'green' //mau cua duong rsi
    };

    $.tickp.cslight = {
        background: '#FBFBFB',
        label: '#080808',
        stroke: '#000000', //'#0B0B0B',
        gridlines: '#111111',
        overlays: ['#CB2BC6', '#5217DB', '#18E197', '#DED71F', '#DE521F', '#10F5B8', '#A6ACE2', '#DF9FB0'], //['#FF6600', '#FFFF33', '#FFFF33', '#00CCFF', '#3366FF'],
        //overlays : ['#50124E','#250966','#075237','#3F3D08', '#3F1607', '#05523E', '#3F4158','#583F45'], //['#663300', '#FFCC00', '#000066', '#00CCFF', '#3366FF'],
        bbands: ['#001122', '#FF9C00', '#001122'],
        macd: ['#000088', '#880000', '#8B008B'],
        psar: '#000099',
        rcandle: '#FF0000',
        gcandle: '#00FF00',
        bodycandle: '#FFFFFF',
        lineplot: '#333333',
        idcss: 'position:absolute; opacity: 0.85;z-index:1;pointer-events: none; border: 1px solid #0066CC; background: #EEE;font-size:10px;font-family:arial,sans-serif;text-align:right;padding:2px;',    //modified nqhuy 13-05-2014
        oloffset: 0,
        colortext: '#121212',
        colorover: '#121212',
        adxcolor: ['#008000', '#FF0000'],
        rsicolor: '#0000FF' //set mau cho rsi
    };

    /* util functions */
    /* Get the canvas for us. */
    function _getCanvas(w, h) {
        c = document.createElement('canvas');
        //c.id = Math.round(Math.random()*100);
        c.id = 100;
        c.width = w;
        c.height = h;
        return c;
    };
    function _minmax2d(data) {
        var max = -Infinity;
        var min = Infinity;

        for (var i in data) {
            for (j in data[i]) {
                if (data[i][j] >= max) max = data[i][j];
                if (data[i][j] < min) min = data[i][j];
            }
        }
        return [min, max];
    };

    function _minmax1d(data) {
        var max = -Infinity;
        var min = Infinity;

        for (var i in data) {
            if (data[i] >= max) max = data[i];
            if (data[i] < min) min = data[i];
        }
        return [min, max];
    };

    function _drawline(ctx, x1, y1, x2, y2, color, width) {
        color = color || "#FF0000";
        var width = width || 1.0;
        var w = ctx.lineWidth;
        ctx.lineWidth = width;
        ctx.strokeStyle = color;
        ctx.beginPath();
        ctx.moveTo(x1, y1);
        ctx.lineTo(x2, y2);
        ctx.stroke();
        ctx.closePath();
        ctx.lineWidth = w;

    };

    // added by huynq 06-08-2014
    function _drawDashedLine(context, fromX, fromY, toX, toY, color, width) {
        context.lineWidth = width;
        context.strokeStyle = color;
        dashPattern = [1, 1];
        context.beginPath();

        var dx = toX - fromX;
        var dy = toY - fromY;
        var angle = Math.atan2(dy, dx);
        var x = fromX;
        var y = fromY;
        context.moveTo(fromX, fromY);
        var idx = 0;
        var draw = true;
        while (!((dx < 0 ? x <= toX : x >= toX) && (dy < 0 ? y <= toY : y >= toY))) {
            var dashLength = dashPattern[idx++ % dashPattern.length];
            var nx = x + (Math.cos(angle) * dashLength);
            x = dx < 0 ? Math.max(toX, nx) : Math.min(toX, nx);
            var ny = y + (Math.sin(angle) * dashLength);
            y = dy < 0 ? Math.max(toY, ny) : Math.min(toY, ny);
            if (draw) {
                context.lineTo(x, y);
            } else {
                context.moveTo(x, y);
            }
            draw = !draw;
        }

        context.closePath();
        context.stroke();
    };

    /* event handlers in the default 'trendline' mode */
    var _beginTrendLine = function (event) {
        var ctx = this.plot.octx;
        ctx.begin_x = event.pageX - this.plot._ElemPageOffsetX(this);
        ctx.begin_y = event.pageY - this.plot._ElemPageOffsetY(this);
        ctx.start = true;
    };

    var _drawTrendLine = function (event) {
        ctx = this.plot.octx;
        var myx = event.pageX - this.plot._ElemPageOffsetX(this);
        var myy = event.pageY - this.plot._ElemPageOffsetY(this);
        if (ctx.start) {
            this.plot.drawshapes();
            ctx.beginPath();
            ctx.strokeStyle = plot.cs.stroke;
            ctx.lineWidth = 1;
            ctx.moveTo(ctx.begin_x, ctx.begin_y);
            ctx.lineTo(myx, myy);
            ctx.stroke();
            ctx.closePath();
        }

        ///
    };

    function _endTrendLine(event) {
        ctx = this.plot.octx;
        ctx.start = false;
        // completed one line 
        var bx = ctx.begin_x;
        var by = ctx.begin_y;
        var ex = this.plot._canvasOffsetX(event.pageX, this);
        var ey = this.plot._canvasOffsetY(event.pageY, this);
        var len = Math.sqrt(Math.pow((ex - bx), 2) + Math.pow((ey - by), 2));
        if (len > 50) {
            this.plot.lines.push([[bx, by], [ex, ey]]);
            this.plot.shapes.push([[bx, by], [ex, ey]]);
            this.plot.lastitem.push(1);
        }
        this.plot.drawshapes();
        this.plot.draw(0); // added huynq 09-07-2014 add tooltip after draw
    };

    var _beginRectangle = function (event) {
        var ctx = this.plot.octx;
        ctx.begin_x = event.pageX - this.plot._ElemPageOffsetX(this);
        ctx.begin_y = event.pageY - this.plot._ElemPageOffsetY(this);
        ctx.start = true;
    };

    var _drawRectangle = function (event) {
        ctx = this.plot.octx;
        var myx = event.pageX - this.plot._ElemPageOffsetX(this);
        var myy = event.pageY - this.plot._ElemPageOffsetY(this);
        if (ctx.start) {
            this.plot.drawshapes();
            ctx.beginPath();
            ctx.strokeStyle = plot.cs.stroke;
            ctx.lineWidth = 1;
            ctx.strokeRect(ctx.begin_x, ctx.begin_y, myx - ctx.begin_x, myy - ctx.begin_y);

            ctx.stroke();
            ctx.closePath();
        }
    };

    function _endRectangle(event) {
        ctx = this.plot.octx;
        ctx.start = false;
        // completed one line 
        var bx = ctx.begin_x;
        var by = ctx.begin_y;
        var ex = this.plot._canvasOffsetX(event.pageX, this);
        var ey = this.plot._canvasOffsetY(event.pageY, this);
        var len = Math.sqrt(Math.pow((ex - bx), 2) + Math.pow((ey - by), 2));
        if (len > 50) {
            this.plot.rectangles.push([[bx, by], [ex, ey]]);
            this.plot.shapes.push([[bx, by], [ex, ey]]);
            this.plot.lastitem.push(2);
        }
        this.plot.drawshapes();
        this.plot.draw(0); // added huynq 09-07-2014 add tooltip after draw
    };

    var _beginEclipse = function (event) {
        var ctx = this.plot.octx;
        ctx.begin_x = event.pageX - this.plot._ElemPageOffsetX(this);
        ctx.begin_y = event.pageY - this.plot._ElemPageOffsetY(this);
        ctx.start = true;
    };

    var _drawEclipse = function (event) {
        ctx = this.plot.octx;
        var myx = event.pageX - this.plot._ElemPageOffsetX(this);
        var myy = event.pageY - this.plot._ElemPageOffsetY(this);
        if (ctx.start) {
            this.plot.drawshapes();
            //ctx.beginPath();
            ctx.strokeStyle = plot.cs.stroke;
            ctx.lineWidth = 1;
            //ctx.strokeRect(ctx.begin_x, ctx.begin_y, myx - ctx.begin_x, myy - ctx.begin_y);
            var x = Math.min(myx, ctx.begin_x);
            var y = Math.min(myy, ctx.begin_y);

            var w = Math.abs(myx - ctx.begin_x);
            var h = Math.abs(myy - ctx.begin_y);
            drawEllipse(ctx, x, y, w, h);

            //ctx.stroke();
            //ctx.closePath();
        }
    };

    function drawEllipse(ctx, x, y, w, h) {
        var kappa = .5522848,
            ox = (w / 2) * kappa, // control point offset horizontal
          oy = (h / 2) * kappa, // control point offset vertical
          xe = x + w,           // x-end
          ye = y + h,           // y-end
          xm = x + w / 2,       // x-middle
          ym = y + h / 2;       // y-middle

        ctx.beginPath();
        ctx.moveTo(x, ym);
        ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
        ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
        ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
        ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
        ctx.closePath();
        ctx.stroke();
    }

    function _endEclipse(event) {
        ctx = this.plot.octx;
        ctx.start = false;
        // completed one line 
        var bx = ctx.begin_x;
        var by = ctx.begin_y;
        var ex = this.plot._canvasOffsetX(event.pageX, this);
        var ey = this.plot._canvasOffsetY(event.pageY, this);
        var len = Math.sqrt(Math.pow((ex - bx), 2) + Math.pow((ey - by), 2));
        if (len > 50) {
            this.plot.eclipses.push([[bx, by], [ex, ey]]);
            this.plot.shapes.push([[bx, by], [ex, ey]]);
            this.plot.lastitem.push(3);
        }
        this.plot.drawshapes();
        this.plot.draw(0); // added huynq 09-07-2014 add tooltip after draw
    };



    var _beginFibFan = function (event) {
        var ctx = this.plot.octx;
        ctx.begin_x = event.pageX - this.plot._ElemPageOffsetX(this);
        ctx.begin_y = event.pageY - this.plot._ElemPageOffsetY(this);
        ctx.start = true;
    };

    var _drawFibFan = function (event) {
        ctx = this.plot.octx;
        var myx = event.pageX - this.plot._ElemPageOffsetX(this);
        var myy = event.pageY - this.plot._ElemPageOffsetY(this);
        if (ctx.start) {
            //this.plot.drawfibfans();
            this.plot.drawshapes();

            var sX = ctx.begin_x, sY = ctx.begin_y, mX = myx, mY = myy;
            ctx.beginPath();
            ctx.moveTo(sX, sY);
            ctx.lineTo(mX, mY);
            ctx.lineWidth = 0.3;
            ctx.stroke();
            ctx.closePath();


            ctx.beginPath();
            ctx.lineWidth = 0.5;
            ctx.font = "normal 10px Verdana";
            ctx.fillStyle = "#FF0000";

            var h = Math.abs(mY - sY);
            var d = Math.abs(mX - sX);

            if (mX < sX && mY < sY) { // right to left and bottom to top
                var tmpx = mX, tmpy = mY;
                mX = sX; mY = sY;
                sX = tmpx; sY = tmpy;
                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY + h * .382 * 2);
                ctx.fillText("61.8%", mX, sY + h * .382);

                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY + h * .5 * 2);
                ctx.fillText("50%", mX, sY + h * .5);

                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY + h * .618 * 2);
                ctx.fillText("38.2%", mX, sY + h * .618);
                //                ctx.moveTo(mX, mY);
                //                ctx.lineTo(sX, sY - h * .382);
                //                ctx.fillText("38.2%", sX, sY - h * .382);

                //                ctx.moveTo(mX, mY);
                //                ctx.lineTo(sX, sY - h * .5);
                //                ctx.fillText("50%", sX, sY - h * .5);

                //                ctx.moveTo(mX, mY);
                //                ctx.lineTo(sX + d, sY - h * .618);
                //                ctx.fillText("61.8%", sX, sY - h * .618);
            }
            else if (mX <= sX && mY >= sY) { // right to left and top to bottom
                var tmpx = mX, tmpy = mY;
                mX = sX; mY = sY;
                sX = tmpx; sY = tmpy;
                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY - h * .382 * 2);
                ctx.fillText("61.8%", mX, sY - h * .382);

                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY - h * .5 * 2);
                ctx.fillText("50%", mX, sY - h * .5);

                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY - h * .618 * 2);
                ctx.fillText("38.2%", mX, sY - h * .618);
                //                ctx.moveTo(mX, mY);
                //                ctx.lineTo(sX , sY + h * .382 );
                //                ctx.fillText("38.2%", sX, sY + h * .382);

                //                ctx.moveTo(mX, mY);
                //                ctx.lineTo(sX , sY + h * .5 );
                //                ctx.fillText("50%", sX, sY + h * .5);

                //                ctx.moveTo(mX, mY);
                //                ctx.lineTo(sX , sY + h * .618 );
                //                ctx.fillText("61.8%", sX, sY + h * .618);
            }
            else if (mX >= sX && mY >= sY) { // left to right and top to bottom
                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY + h * .382 * 2);
                ctx.fillText("61.8%", mX, sY + h * .382);

                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY + h * .5 * 2);
                ctx.fillText("50%", mX, sY + h * .5);

                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY + h * .618 * 2);
                ctx.fillText("38.2%", mX, sY + h * .618);
            }
            else if (mX >= sX && mY < sY) { // left to right and bottom to top
                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY - h * .382 * 2);
                ctx.fillText("61.8%", mX, sY - h * .382);

                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY - h * .5 * 2);
                ctx.fillText("50%", mX, sY - h * .5);

                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY - h * .618 * 2);
                ctx.fillText("38.2%", mX, sY - h * .618);
            }

            ctx.stroke();
            ctx.closePath();
        }
    };

    function _endFibFan(event) {
        ctx = this.plot.octx;
        ctx.start = false;
        // completed one line 
        var bx = ctx.begin_x;
        var by = ctx.begin_y;
        var ex = this.plot._canvasOffsetX(event.pageX, this);
        var ey = this.plot._canvasOffsetY(event.pageY, this);
        var len = Math.sqrt(Math.pow((ex - bx), 2) + Math.pow((ey - by), 2));
        if (len > 50) {
            this.plot.fibfans.push([[bx, by], [ex, ey]]);
            this.plot.shapes.push([[bx, by], [ex, ey]]);
            this.plot.lastitem.push(4);
        }
        //this.plot.drawfibfans();
        this.plot.drawshapes();
        this.plot.draw(0); // added huynq 09-07-2014 add tooltip after draw
    };

    var _beginFibRec = function (event) {
        var ctx = this.plot.octx;
        ctx.begin_x = event.pageX - this.plot._ElemPageOffsetX(this);
        ctx.begin_y = event.pageY - this.plot._ElemPageOffsetY(this);
        ctx.start = true;
    };

    var _drawFibRec = function (event) {
        ctx = this.plot.octx;
        var myx = event.pageX - this.plot._ElemPageOffsetX(this);
        var myy = event.pageY - this.plot._ElemPageOffsetY(this);
        if (ctx.start) {
            //this.plot.drawfibfans();
            this.plot.drawshapes();

            var sX = ctx.begin_x, sY = ctx.begin_y, mX = myx, mY = myy;
            ctx.beginPath();
            ctx.moveTo(sX, sY);
            ctx.lineTo(mX, mY);
            ctx.lineWidth = 0.3;
            ctx.stroke();
            ctx.closePath();


            ctx.beginPath();
            ctx.lineWidth = 0.5;
            ctx.font = "normal 10px Verdana";
            ctx.fillStyle = "#FF0000";

            var h = Math.abs(mY - sY);
            var d = Math.abs(mX - sX); // added huynq 2014-09-11 double range +d

            if (mX < sX && mY < sY) { // right to left and bottom to top 
                ctx.moveTo(mX, mY);
                ctx.lineTo(sX + d, mY);
                ctx.fillText("100%", sX, mY);

                ctx.moveTo(mX, sY);
                ctx.lineTo(sX + d, sY);
                ctx.fillText("0%", sX, sY);

                ctx.lineWidth = 0.3;
                ctx.moveTo(mX, mY + h / 2);
                ctx.lineTo(sX + d, mY + h / 2);
                ctx.fillText("50%", sX, mY + h / 2);

                ctx.moveTo(mX, mY + h * .382);
                ctx.lineTo(sX + d, mY + h * .382);
                ctx.fillText("61.8%", sX, mY + h * .382);

                ctx.moveTo(mX, mY + h * .618);
                ctx.lineTo(sX + d, mY + h * .618);
                ctx.fillText("38.2%", sX, mY + h * .618);
            }
            else if (mX <= sX && mY >= sY) { // right to left and top to bottom
                ctx.moveTo(mX, sY);
                ctx.lineTo(sX + d, sY);
                ctx.fillText("0%", sX, sY);

                ctx.moveTo(mX, mY);
                ctx.lineTo(sX + d, mY);
                ctx.fillText("100%", sX, mY);

                ctx.lineWidth = 0.3;
                ctx.moveTo(mX, sY + h / 2);
                ctx.lineTo(sX + d, sY + h / 2);
                ctx.fillText("50%", sX, sY + h / 2);

                ctx.moveTo(mX, sY + h * .382);
                ctx.lineTo(sX + d, sY + h * .382);
                ctx.fillText("38.2%", sX, sY + h * .382);

                ctx.moveTo(mX, sY + h * .618);
                ctx.lineTo(sX + d, sY + h * .618);
                ctx.fillText("61.8%", sX, sY + h * .618);
            }
            else if (mX >= sX && mY >= sY) { // left to right and top to bottom
                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY);
                ctx.fillText("100%", mX, sY);

                ctx.moveTo(sX, mY);
                ctx.lineTo(mX + d, mY);
                ctx.fillText("0%", mX, mY);

                ctx.lineWidth = 0.3;
                ctx.moveTo(sX, sY + h / 2);
                ctx.lineTo(mX + d, sY + h / 2);
                ctx.fillText("50%", mX, sY + h / 2);

                ctx.moveTo(sX, sY + h * .382);
                ctx.lineTo(mX + d, sY + h * .382);
                ctx.fillText("61.8%", mX, sY + h * .382);

                ctx.moveTo(sX, sY + h * .618);
                ctx.lineTo(mX + d, sY + h * .618);
                ctx.fillText("38.2%", mX, sY + h * .618);
            }
            else if (mX >= sX && mY < sY) { // left to right and bottom to top
                ctx.moveTo(sX, sY);
                ctx.lineTo(mX + d, sY);
                ctx.fillText("100%", mX, sY);

                ctx.moveTo(sX, mY);
                ctx.lineTo(mX + d, mY);
                ctx.fillText("0%", mX, mY);

                ctx.lineWidth = 0.3;
                ctx.moveTo(sX, mY + h / 2);
                ctx.lineTo(mX + d, mY + h / 2);
                ctx.fillText("50%", mX, mY + h / 2);

                ctx.moveTo(sX, mY + h * .382);
                ctx.lineTo(mX + d, mY + h * .382);
                ctx.fillText("38.2%", mX, mY + h * .382);

                ctx.moveTo(sX, mY + h * .618);
                ctx.lineTo(mX + d, mY + h * .618);
                ctx.fillText("61.8%", mX, mY + h * .618);
            }

            ctx.stroke();
            ctx.closePath();
        }
    };

    function _endFibRec(event) {
        ctx = this.plot.octx;
        ctx.start = false;
        // completed one line 
        var bx = ctx.begin_x;
        var by = ctx.begin_y;
        var ex = this.plot._canvasOffsetX(event.pageX, this);
        var ey = this.plot._canvasOffsetY(event.pageY, this);
        var len = Math.sqrt(Math.pow((ex - bx), 2) + Math.pow((ey - by), 2));
        if (len > 50) {
            this.plot.fibrecs.push([[bx, by], [ex, ey]]);
            this.plot.shapes.push([[bx, by], [ex, ey]]);
            this.plot.lastitem.push(5);
        }
        //this.plot.drawfibfans();
        this.plot.drawshapes();
        this.plot.draw(0); // added huynq 09-07-2014 add tooltip after draw
    };

    function _undo() {
        var p = this.plot;
        var shape = p.shapes.pop();
        //if (shape)
        //    p.undoshapes.unshift(shape);
        var item = p.lastitem.pop();
        if (item == 1)
            p.lines.pop(shape);
        else if (item == 2)
            p.rectangles.pop(shape);
        else if (item == 3)
            p.eclipses.pop(shape);
        else if (item == 4)
            p.fibfans.pop(shape);
        else if (item == 5)
            p.fibrecs.pop(shape);
        p.drawshapes();
        this.plot.draw(0); // added huynq 12-09-2014 add tooltip after draw
    };

    function _keyActions(event) {
        var p = this.plot;
        if (event.ctrlKey) {
            if (event.keyCode === 90) {

                var shape = p.shapes.pop();
                //if (shape)
                //    p.undoshapes.unshift(shape);
                var item = p.lastitem.pop();
                if (item == 1)
                    p.lines.pop(shape);
                else if (item == 2)
                    p.rectangles.pop(shape);
                else if (item == 3)
                    p.eclipses.pop(shape);
                else if (item == 4)
                    p.fibfans.pop(shape);
                else if (item == 5)
                    p.fibrecs.pop(shape);
                p.drawshapes();
            } else if (event.keyCode === 89) {

            }
        } else if (event.altKey) {
            if (event.keyCode === 107) {
                p.zoom(1);
            } else if (event.keyCode === 109) {
                p.zoom(0);
            }
        }

    };

    /* event handlers in the Pan and zoom mode */
    var _beginPanning = function (event) {
        if (this.style != undefined) {
            this.style.cursor = 'move';
        }
        else {
            this.plot.canvas.style.cursor = 'move';
        }
        //added huynq 16-10-2014		FIX for IE9,10
        if (!e) var e = (window.event) ? (window.event) : event;
        var posx = 0;
        var posy = 0;
        if (!e) var e = (window.event) ? (window.event) : event;
        if (e.pageX || e.pageY) {
            posx = e.pageX;
            posy = e.pageY;
        }
        else if (e.clientX || e.clientY) {
            posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }

        var ctx = this.plot.octx;
        ctx.begin_x = this.plot._canvasOffsetX(posx, this.plot.canvas);
        ctx.begin_y = this.plot._canvasOffsetY(posy, this.plot.canvas);
        ctx.start = true;

    };
    var _doPanning = function (event) {

        var p = this.plot;
        var ctx = p.octx;
        var pageX = event.pageX, pageY = event.pageY;

        //added huynq 16-10-2014		FIX for IE9,10
        if (!e) var e = (window.event) ? (window.event) : event;
        var posx = 0;
        var posy = 0;
        if (!e) var e = (window.event) ? (window.event) : event;
        if (e.pageX || e.pageY) {
            posx = e.pageX;
            posy = e.pageY;
        }
        else if (e.clientX || e.clientY) {
            posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }

        var myx = p._canvasOffsetX(posx, this);
        var myy = p._canvasOffsetY(posy, this);
        if (isNaN(myx)) {
            myx = p._canvasOffsetX(posx, this.plot.canvas);
            myy = p._canvasOffsetY(posy, this.plot.canvas);
        }

        var xo = ctx.begin_x == undefined ? 0 : myx - ctx.begin_x;
        var yo = ctx.begin_y == undefined ? 0 : myy - ctx.begin_y;

        if (ctx.start) {
            if (Math.abs(xo) > p.cp.minwidth) {
                size = Math.floor(xo / p.cp.minwidth);
                p._doplot(p.ctx, p.current, size);
                ctx.begin_x = myx;
                ctx.begin_y = myy;
            }
        }
        else {
            this.canvas = _getCanvas(this.width, this.height);
            divObj = document.getElementById("lineTooltip");
            divObjX = document.getElementById("lineTooltipX"); //nhathn them duong line ox

            var cdl = this.plot._getCandle(myx, myy);

            if (myy <= 40 || myy >= this.plot.canvas.height - 20 || myx < 9) { //nhathn chinh khoanh cach an currsor                
                this.plot.infodiv.style.display = 'none';
                divObj.style.display = 'none';
                divObjX.style.display = 'none';
            }
            else if (cdl >= 0 && cdl != null) {

                //this.canvas = _getCanvas(this.width, this.height);
                loadTootip(myx + 40, myy, this.canvas.height, this.canvas.width);
                //wait(500);
                //this.plot._showInfo(cdl, event.pageX + 5, event.pageY + 10); // modified huynq 16-10-2014 FIX for IE9,10
                this.plot._showInfo(cdl, posx, posy + 20);
                divObj.style.display = 'block';
                divObjX.style.display = 'block';
            } else {
                this.plot.infodiv.style.display = 'none';

                divObj.style.display = 'none';
                divObjX.style.display = 'none';
            }


        }
    };
    var _endPanning = function (event) {

        this.plot.octx.start = false;
        if (this.style != undefined) {
            this.style.cursor = 'default';
        }
        else {
            this.plot.canvas.style.cursor = 'default';
        }


    };

    var _showTooltip = function (event) {
        var p = this.plot;
        var ctx = p.octx;
        var pageX = event.pageX, pageY = event.pageY;

        //added huynq 16-10-2014		FIX for IE9,10
        if (!e) var e = (window.event) ? (window.event) : event;
        var posx = 0;
        var posy = 0;
        if (!e) var e = (window.event) ? (window.event) : event;
        if (e.pageX || e.pageY) {
            posx = e.pageX;
            posy = e.pageY;
        }
        else if (e.clientX || e.clientY) {
            posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }

        var myx = p._canvasOffsetX(posx, this);
        var myy = p._canvasOffsetY(posy, this);
        if (isNaN(myx)) {
            myx = p._canvasOffsetX(posx, this.plot.canvas);
            myy = p._canvasOffsetY(posy, this.plot.canvas);
        }

        var xo = ctx.begin_x == undefined ? 0 : myx - ctx.begin_x;
        var yo = ctx.begin_y == undefined ? 0 : myy - ctx.begin_y;

        if (ctx.start) {
            if (Math.abs(xo) > p.cp.minwidth) {
                size = Math.floor(xo / p.cp.minwidth);
                p._doplot(p.ctx, p.current, size);
                ctx.begin_x = myx;
                ctx.begin_y = myy;
            }
        }
        else {
            this.canvas = _getCanvas(this.width, this.height);
            divObj = document.getElementById("lineTooltip");
            divObjX = document.getElementById("lineTooltipX");

            var cdl = this.plot._getCandle(myx, myy);

            if (myy <= 40 || myy >= this.plot.canvas.height - 20 || myx < 20) {
                this.plot.infodiv.style.display = 'none';
                divObj.style.display = 'none';
                divObjX.style.display = 'none';
            }
            else if (cdl >= 0 && cdl != null) {
                //this.canvas = _getCanvas(this.width, this.height);
                loadTootip(myx + 40, myy, this.canvas.height, this.canvas.width);
                //wait(500);
                //this.plot._showInfo(cdl, event.pageX + 5, event.pageY + 10); // modified huynq 16-10-2014 FIX for IE9,10
                this.plot._showInfo(cdl, posx, posy + 20);
                divObj.style.display = 'block';
                divObjX.style.display = 'block';
            } else {
                this.plot.infodiv.style.display = 'none';

                divObj.style.display = 'none';
                divObjX.style.display = 'none';
            }
        }
    }
    function wait(msecs) {
        var start = new Date().getTime();
        var cur = start;
        while (cur - start < msecs) {
            cur = new Date().getTime();
        } return true;
    }

    // Function below is not perfect. But as close to usable. 
    // Some Date formats like '12-Oct' fail. But yes, thats understandable.
    function _getDateTs(str) {
        var dateStr = str; //use code for IE
        var a, d1;
        if (dateStr.length > 10) {
            a = dateStr.split(" ");
            d1 = a[0].split("-");
            var t = a[1].split(":");
            var d2 = new Date(d1[0], (d1[1] - 1), d1[2], t[0], t[1]);
        }
        else {
            a = dateStr.split(" ");
            d1 = a[0].split("-");
            var d2 = new Date(d1[0], (d1[1] - 1), d1[2]); //new Date(str); error on safari
        }

        var d;
        d = d2.getTime();
        if (!isNaN(d)) {
            return d;
        }
        str = str.replace(/-/g, ' '); //1 Jan 2010 works but 1-Jan-2010 doesn't
        d = new Date(str).getTime();
        if (!isNaN(d)) {
            return d;
        }
        // may be what we've is a time stamp. 
        if ((d = parseInt(str)) > 100000) {
            // we are not handling something that's up on 1st Jan 1971, as yet.
            // assume it is a valid time stamp and just send it back.
            return d;
        }
    };

    function _tsToDate(ts) {
        var d = new Date(ts);
        var dd = d.getDate();
        var mm = d.getMonth() + 1;
        var hh = d.getHours();
        hh = hh < 10 ? '0' + hh : hh;
        var mi = d.getMinutes();
        mi = mi < 10 ? '0' + mi : mi;
        dd = (dd >= 10 ? dd : '0' + dd);
        mm = (mm >= 10 ? mm : '0' + mm);
        yy = d.getFullYear();

        //return yy + '-' + mm + '-' + dd; // modified by nqhuy 12-05-2014
        return hh == '00' ? (dd + '-' + mm + '-' + yy) : (dd + '-' + mm + '-' + yy + ' ' + hh + ':' + mi);
    };

    // The following validation should ideally be done by the client who calls us
    // but, let's not assume client actually validates, we validate is again.
    // better idea may be to move such functions into utils and have 'optionally' include utils 
    function validParams(type, params) {
        var notalpha = /[^a-z]+/g
        var notnumeric = /[^0-9]+/g
        var isfloat = /(^\.|[0-9]+\.)[0-9]+$/g
        switch (type) {
            case 'ema':
            case 'sma':
                if (params.length != 1) {
                    return false;
                }
                if (params[0].match(notnumeric)) {
                    return false;
                }
                return true;
            case 'bbands':
            case 'psar':
                if (params.length != 2) {
                    vmsg = 'Invalid length of params: expected 2, received ' + params.length;
                    return false;
                }
                for (var i in params) {
                    if (params[i].match(notnumeric) && !params[i].match(isfloat)) {
                        return false;
                    }
                }
                return true;

            case 'stoch':
            case 'macd':
            case 'uo': // added huynq 21-05-2014
                if (params.length != 3) {
                    vmsg = 'Invalid length of params: expected 3, received ' + params.length;
                    return false;
                }
                for (var i in params) {
                    if (params[i].match(notnumeric)) {
                        return false;
                    }
                }
                return true;

            case 'rsi':
            case 'adx': // added nqhuy 14-05-2014
            case 'momentum': // added huynq 22-05-2014
            case 'mfi': // added huynq 22-05-2014
            case 'cmf': // added huynq 22-05-2014
            case 'perR': // added huynq 23-05-2014
            case 'atr': // added huynq 23-05-2014
            case 'cci': // added huynq 26-05-2014
                if (params.length != 1) {
                    vmsg = 'Invalid length of params: expected 1, received ' + params.length;
                    return false;
                }
                if (params[0].match(notnumeric)) {
                    return false;
                }
                return true;

            case 'obv': // added huynq 23-05-2014
                return true;

            default:
                return false;
        }

        // if we come here, something is wrong, so let's return false
        return false;
    };

    function loadTootip(posX, poxY, height, width) {
        //nhathn
        posX = posX - 40;
        divObj = document.getElementById("lineTooltip");
        divObj.offsetHeight = height;
        divObj.style.left = posX + 'px';
        divObj.style.height = 375 + 'px';
        divObj.style.top = 20 + 'px'; //'40px'; // rat kho dieu chinh tu dong // modified huynq 08-08-2014
        var e = document.getElementById("100");

        divObjX = document.getElementById("lineTooltipX");        //them duong line ox
        divObjX.style.top = poxY + 'px';
        divObjX.style.width = 724 + 'px';
        divObjX.style.left = 5 + 'px';
        //nhathn add them duong line Ox
    }

    /* Following functions are used from jQuery 
    The reason for doing this
    1. jquery does it properly 
    2. Including jquery for three functions is kind of an overkill. So 
    we'd keep including functions from jquery in here. If and when
    this becomes too big to be so, we'd just use jQuery library.

        Copyright 2010, John Resig (http://jquery.org/license)

    */
    function parseJSON(data) {
        if (typeof data !== "string" || !data) {
            return null;
        }

        var rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
        // Make sure leading/trailing whitespace is removed (IE can't handle it)
		data = data.replace(rtrim, "");

        // Make sure the incoming data is actual JSON
        // Logic borrowed from http://json.org/json2.js
        if (/^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
			.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
			.replace(/(?:^|:|,)(?:\s*\[)+/g, ""))) {

            // Try to use the native JSON parser first
            return window.JSON && window.JSON.parse ?
				window.JSON.parse(data) :
				(new Function("return " + data))();

        } else {
            throw "JSON parse error:";
        }
    };

    // added nqhuy 13-05-2014 (format Volume)
    //function formatVolume(n) {
    //    return n.toFixed(0).replace(/./g, function (c, i, a) {
    //        return i > 0 && c !== "." && (a.length - i) % 3 === 0 ? "," + c : c;
    //    });
    //}





    function format_number(dec, fix) {
        var fixValue = parseFloat(Math.pow(10, fix));
        var retValue = parseInt(Math.ceil(dec * fixValue)) / fixValue;
        return Math.ceil(retValue);
    }

    function numberWithCommas(x) {
        return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }

    function str_replace(haystack, needle, replacement) {
        var temp = haystack.split(needle);
        return temp.join(replacement);
    }

    function isArray(a) {
        return Object.prototype.toString.call(a) === "[object Array]";
    };
    function isFunction(f) {
        return Object.prototype.toString.call(f) === "[object Function]";
    };

    // Our own lame selector. 
    function $$(selector) {
        if (typeof selector !== "string" || !selector) {
            return e;
        }

        // someone gave us #id or div#id. just take the id part out. 
        var i = selector.search('#');
        if (i !== -1) {
            id = selector.substring(i + 1);
            return document.getElementById(id);
        } else {
            // we still try by ID in case someone forgot to send the #
            var e = document.getElementById(selector);
            if (!e) {
                // first of all elements by given name  
                e = document.getElementsByName(selector)[0];
            }
            // we return whatever we got . 
            return e;
        }
    };
})(window);
